aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Smirnov <alex@ydb.tech>2025-03-04 13:27:53 +0000
committerAlexander Smirnov <alex@ydb.tech>2025-03-04 13:27:53 +0000
commitad0b83372abbcedc2887412cfdd967ea22b7148e (patch)
treed24fee47ed2aef834af220ac137f5a0e26ed4678
parent31ac77ec345d18126d79b7797bef365c9068d999 (diff)
parent040bd4000eeec19eab31a023f72c3c06494ce92d (diff)
downloadydb-ad0b83372abbcedc2887412cfdd967ea22b7148e.tar.gz
Merge pull request #15226 from ydb-platform/merge-libs-250302-1120
-rw-r--r--.github/config/muted_ya.txt3
-rw-r--r--build/conf/docs.conf9
-rw-r--r--build/conf/go.conf2
-rw-r--r--build/conf/java.conf5
-rw-r--r--build/conf/opensource.conf6
-rw-r--r--build/conf/python.conf2
-rw-r--r--build/conf/settings.conf5
-rw-r--r--build/plugins/_dart_fields.py21
-rw-r--r--build/plugins/lib/test_const/__init__.py1
-rw-r--r--build/plugins/ytest.py13
-rw-r--r--build/scripts/fetch_resource.py43
-rw-r--r--build/scripts/ya.make1
-rw-r--r--build/sysincl/emscripten.yml3
-rw-r--r--build/ymake.core.conf15
-rw-r--r--contrib/libs/cxxsupp/libcxxrt/.yandex_meta/override.nix6
-rw-r--r--contrib/libs/cxxsupp/libcxxrt/exception.cc93
-rw-r--r--contrib/libs/cxxsupp/libcxxrt/patches/do-upcast-not-cast-to.patch17
-rw-r--r--contrib/libs/cxxsupp/libcxxrt/patches/pr41.1-move-trace.patch74
-rw-r--r--contrib/libs/cxxsupp/libcxxrt/patches/pr41.2-fix-trace-format.patch48
-rw-r--r--contrib/libs/cxxsupp/libcxxrt/patches/pr49-cosmetics.patch24
-rw-r--r--contrib/libs/cxxsupp/libcxxrt/ya.make4
-rw-r--r--contrib/libs/llvm14/lib/CodeGen/MIRParser/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/DWARFLinker/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/DWP/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/DebugInfo/GSYM/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/DebugInfo/PDB/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/DebugInfo/Symbolize/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/ExecutionEngine/Interpreter/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/ExecutionEngine/JITLink/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/ExecutionEngine/Orc/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Extensions/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/FileCheck/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Frontend/OpenACC/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/FuzzMutate/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/InterfaceStub/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/LTO/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/LineEditor/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/MCA/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/ObjectYAML/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Option/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Passes/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/ProfileData/Coverage/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/AArch64/AsmParser/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/AArch64/Disassembler/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/AArch64/TargetInfo/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/AArch64/Utils/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/AArch64/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/ARM/AsmParser/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/ARM/Disassembler/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/ARM/TargetInfo/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/ARM/Utils/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/ARM/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/BPF/AsmParser/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/BPF/Disassembler/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/BPF/TargetInfo/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/BPF/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/NVPTX/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/PowerPC/AsmParser/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/PowerPC/Disassembler/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Target/PowerPC/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/ToolDrivers/llvm-dlltool/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/ToolDrivers/llvm-lib/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/Transforms/Coroutines/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/WindowsManifest/ya.make2
-rw-r--r--contrib/libs/llvm14/lib/XRay/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/gold/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llc/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/lli/ChildTarget/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/lli/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-ar/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-as/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-bcanalyzer/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-cat/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-cfi-verify/lib/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-cfi-verify/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-config/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-cvtres/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxdump/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxfilt/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxmap/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-diff/lib/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-diff/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-dis/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-dwarfdump/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-dwp/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-extract/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-gsymutil/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-ifs/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-jitlink/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-libtool-darwin/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-link/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-lipo/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-lto/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-lto2/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-mc/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-ml/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-modextract/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-mt/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-nm/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-opt-report/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-profdata/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-rtdyld/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-size/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-split/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-stress/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-strings/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-symbolizer/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-undname/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/lto/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/obj2yaml/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/opt/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/remarks-shlib/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/sancov/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/sanstats/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/split-file/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/verify-uselistorder/ya.make2
-rw-r--r--contrib/libs/llvm14/tools/yaml2obj/ya.make2
-rw-r--r--contrib/restricted/googletest/googletest/include/gtest/gtest-printers.h5
-rw-r--r--library/cpp/http/simple/http_client.h25
-rw-r--r--library/cpp/neh/http_common.cpp27
-rw-r--r--library/cpp/neh/http_common.h3
-rw-r--r--library/cpp/neh/https.cpp32
-rw-r--r--library/cpp/tdigest/tdigest.cpp44
-rw-r--r--library/cpp/tdigest/tdigest.h10
-rw-r--r--library/cpp/tdigest/tdigest.proto1
-rw-r--r--library/cpp/tld/tlds-alpha-by-domain.txt4
-rw-r--r--library/cpp/yt/error/error.cpp32
-rw-r--r--library/cpp/yt/error/error.h10
-rw-r--r--library/python/runtime_py3/main/main.c31
-rw-r--r--util/system/backtrace.cpp2
-rw-r--r--util/system/thread.cpp4
-rw-r--r--util/ya.make2
-rw-r--r--ydb/ci/rightlib.txt2
-rw-r--r--ydb/library/yaml_config/ut_transform/canondata/test_transform.TestYamlConfigTransformations.test_basic_args1-dump_ds_init_/nvme.yaml.result.json2
-rw-r--r--yql/essentials/core/facade/ya.make6
-rw-r--r--yql/essentials/core/facade/yql_facade.cpp10
-rw-r--r--yql/essentials/minikql/computation/llvm16/ya.make17
-rw-r--r--yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/epilogue.cmake8
-rw-r--r--yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/ya.make43
-rw-r--r--yql/essentials/parser/antlr_ast/gen/v1_antlr4/epilogue.cmake8
-rw-r--r--yql/essentials/parser/antlr_ast/gen/v1_antlr4/ya.make43
-rw-r--r--yql/essentials/parser/antlr_ast/gen/ya.make4
-rw-r--r--yql/essentials/parser/antlr_ast/org/antlr/v4/tool/templates/codegen/Cpp/Cpp.stg1176
-rw-r--r--yql/essentials/parser/antlr_ast/org/antlr/v4/tool/templates/codegen/Cpp/Files.stg344
-rw-r--r--yql/essentials/parser/antlr_ast/ya.make4
-rw-r--r--yql/essentials/parser/ya.make1
-rw-r--r--yql/essentials/public/fastcheck/translator.cpp11
-rw-r--r--yql/essentials/public/purecalc/common/worker_factory.cpp12
-rw-r--r--yql/essentials/public/purecalc/common/ya.make.inc6
-rw-r--r--yql/essentials/sql/v1/SQLv1.g.in5
-rw-r--r--yql/essentials/sql/v1/SQLv1Antlr4.g.in5
-rw-r--r--yql/essentials/sql/v1/complete/c3_engine.h116
-rw-r--r--yql/essentials/sql/v1/complete/sql_antlr4.cpp116
-rw-r--r--yql/essentials/sql/v1/complete/sql_antlr4.h28
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete.cpp89
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete.h44
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete_ut.cpp323
-rw-r--r--yql/essentials/sql/v1/complete/sql_context.cpp123
-rw-r--r--yql/essentials/sql/v1/complete/sql_context.h23
-rw-r--r--yql/essentials/sql/v1/complete/sql_syntax.cpp19
-rw-r--r--yql/essentials/sql/v1/complete/sql_syntax.h10
-rw-r--r--yql/essentials/sql/v1/complete/string_util.cpp29
-rw-r--r--yql/essentials/sql/v1/complete/string_util.h17
-rw-r--r--yql/essentials/sql/v1/complete/string_util_ut.cpp21
-rw-r--r--yql/essentials/sql/v1/complete/ut/ya.make8
-rw-r--r--yql/essentials/sql/v1/complete/ya.make24
-rw-r--r--yql/essentials/sql/v1/sql_translation.cpp41
-rw-r--r--yql/essentials/sql/v1/sql_ut.cpp8155
-rw-r--r--yql/essentials/sql/v1/sql_ut_antlr4.cpp8169
-rw-r--r--yql/essentials/sql/v1/sql_ut_antlr4.h8
-rw-r--r--yql/essentials/sql/v1/sql_ut_common.h8398
-rw-r--r--yql/essentials/sql/v1/ya.make1
-rw-r--r--yql/essentials/tests/common/test_framework/yql_utils.py2
-rw-r--r--yql/essentials/tools/ya.make1
-rw-r--r--yql/essentials/tools/yql_complete/ya.make12
-rw-r--r--yql/essentials/tools/yql_complete/yql_complete.cpp53
-rw-r--r--yql/essentials/udfs/common/datetime2/datetime_udf.cpp377
-rw-r--r--yql/essentials/udfs/common/datetime2/test_bigdates/canondata/result.json5
-rw-r--r--yql/essentials/udfs/common/datetime2/test_bigdates/canondata/test.test_To_/results.txt826
-rw-r--r--yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.cfg1
-rw-r--r--yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.in72
-rw-r--r--yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.in.attr59
-rw-r--r--yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.sql40
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_callable_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_cast.cpp23
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_cast_ut.cpp41
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_decimal_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_dict_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_list_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_number_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_optional_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_resource_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_stream_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_string_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_struct_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_test_engine.h (renamed from yql/essentials/udfs/common/python/bindings/ut3/py_test_engine.h)2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_tuple_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_tzdate_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_variant_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/py_void_ut.cpp2
-rw-r--r--yql/essentials/udfs/common/python/bindings/ut2/ya.make9
-rw-r--r--yql/essentials/udfs/common/python/bindings/ut3/ya.make29
-rw-r--r--yql/essentials/udfs/common/python/bindings/ya.make1
-rw-r--r--yql/essentials/udfs/common/python/bindings/ya.make.test.inc27
-rw-r--r--yql/essentials/udfs/common/python/python_udf/python_udf.cpp54
-rw-r--r--yql/essentials/udfs/common/python/python_udf/python_udf.h55
-rw-r--r--yql/essentials/udfs/common/stat/static/stat_udf.h9
-rw-r--r--yql/essentials/udfs/common/stat/test/canondata/result.json7
-rw-r--r--yql/essentials/udfs/common/stat/test/canondata/test.test_nan_/results.txt46
-rw-r--r--yql/essentials/udfs/common/stat/test/cases/nan.sql2
-rw-r--r--yql/essentials/udfs/common/stat/test/ya.make13
-rw-r--r--yql/essentials/udfs/common/stat/ya.make1
-rw-r--r--yt/cpp/mapreduce/client/client.cpp37
-rw-r--r--yt/cpp/mapreduce/client/client.h4
-rw-r--r--yt/cpp/mapreduce/client/client_reader.cpp2
-rw-r--r--yt/cpp/mapreduce/client/operation.cpp19
-rw-r--r--yt/cpp/mapreduce/client/skiff.cpp15
-rw-r--r--yt/cpp/mapreduce/client/skiff.h1
-rw-r--r--yt/cpp/mapreduce/client/structured_table_formats.cpp25
-rw-r--r--yt/cpp/mapreduce/client/structured_table_formats.h2
-rw-r--r--yt/cpp/mapreduce/http/context.cpp3
-rw-r--r--yt/cpp/mapreduce/http/context.h1
-rw-r--r--yt/cpp/mapreduce/http_client/raw_client.cpp5
-rw-r--r--yt/cpp/mapreduce/http_client/raw_client.h2
-rw-r--r--yt/cpp/mapreduce/http_client/raw_requests.h48
-rw-r--r--yt/cpp/mapreduce/interface/common.cpp22
-rw-r--r--yt/cpp/mapreduce/interface/common.h9
-rw-r--r--yt/cpp/mapreduce/interface/raw_client.h7
-rw-r--r--yt/python/yt/common.py4
-rw-r--r--yt/yql/providers/yt/codec/codegen/llvm14/ya.make14
-rw-r--r--yt/yql/providers/yt/codec/codegen/ut/llvm14/ya.make12
-rw-r--r--yt/yql/providers/yt/codec/codegen/ut/ya.make4
-rw-r--r--yt/yql/providers/yt/codec/codegen/ya.make5
-rw-r--r--yt/yql/providers/yt/comp_nodes/dq/llvm14/ya.make11
-rw-r--r--yt/yql/providers/yt/comp_nodes/dq/ya.make4
-rw-r--r--yt/yql/providers/yt/comp_nodes/llvm14/ya.make13
-rw-r--r--yt/yql/providers/yt/comp_nodes/ut/llvm14/ya.make11
-rw-r--r--yt/yql/providers/yt/comp_nodes/ut/ya.make4
-rw-r--r--yt/yql/providers/yt/comp_nodes/ya.make1
-rw-r--r--yt/yql/providers/yt/gateway/file/yql_yt_file.cpp11
-rw-r--r--yt/yql/providers/yt/gateway/native/yql_yt_native.cpp111
-rw-r--r--yt/yql/providers/yt/provider/phy_opt/yql_yt_phy_opt_sort.cpp49
-rw-r--r--yt/yql/tests/sql/suites/order_by/input_sorted_desc.txt3
-rw-r--r--yt/yql/tests/sql/suites/order_by/input_sorted_desc.txt.attr61
-rw-r--r--yt/yql/tests/sql/suites/order_by/yql-19598.cfg1
-rw-r--r--yt/yql/tests/sql/suites/order_by/yql-19598.sql4
-rw-r--r--yt/yt/client/api/public.h2
-rw-r--r--yt/yt/client/api/queue_transaction.h1
-rw-r--r--yt/yt/client/api/rpc_proxy/client_base.cpp68
-rw-r--r--yt/yt/client/api/rpc_proxy/client_impl.cpp141
-rw-r--r--yt/yt/client/api/rpc_proxy/helpers.cpp473
-rw-r--r--yt/yt/client/api/rpc_proxy/table_mount_cache.cpp4
-rw-r--r--yt/yt/client/driver/etc_commands.cpp9
-rw-r--r--yt/yt/client/driver/etc_commands.h2
-rw-r--r--yt/yt/client/driver/proxy_discovery_cache.cpp16
-rw-r--r--yt/yt/client/driver/proxy_discovery_cache.h2
-rw-r--r--yt/yt/client/node_tracker_client/node_directory.cpp26
-rw-r--r--yt/yt/client/object_client/helpers.cpp3
-rw-r--r--yt/yt/client/object_client/public.h4
-rw-r--r--yt/yt/client/security_client/public.h6
-rw-r--r--yt/yt/client/sequoia_client/public.h7
-rw-r--r--yt/yt/client/table_client/config.cpp4
-rw-r--r--yt/yt/client/table_client/config.h3
-rw-r--r--yt/yt/client/table_client/helpers.cpp17
-rw-r--r--yt/yt/client/table_client/helpers.h15
-rw-r--r--yt/yt/client/table_client/schema.cpp54
-rw-r--r--yt/yt/core/concurrency/fls-inl.h6
-rw-r--r--yt/yt/core/concurrency/fls.h1
-rw-r--r--yt/yt/core/misc/error.cpp99
-rw-r--r--yt/yt/core/misc/error.h52
-rw-r--r--yt/yt/core/misc/protobuf_helpers-inl.h2
-rw-r--r--yt/yt/core/misc/protobuf_helpers.h28
-rw-r--r--yt/yt/core/misc/unittests/error_ut.cpp14
-rw-r--r--yt/yt/core/test_framework/framework.cpp2
-rw-r--r--yt/yt/core/ytree/node_detail.cpp2
-rw-r--r--yt/yt/core/ytree/request_complexity_limits.cpp4
-rw-r--r--yt/yt/core/ytree/unittests/yson_struct_update_ut.cpp31
-rw-r--r--yt/yt/core/ytree/yson_struct_update-inl.h69
-rw-r--r--yt/yt/core/ytree/yson_struct_update.h34
307 files changed, 14150 insertions, 17715 deletions
diff --git a/.github/config/muted_ya.txt b/.github/config/muted_ya.txt
index 591d7b665c..52651e4221 100644
--- a/.github/config/muted_ya.txt
+++ b/.github/config/muted_ya.txt
@@ -85,6 +85,7 @@ ydb/library/actors/http/ut sole chunk chunk
ydb/library/actors/http/ut sole+chunk+chunk
ydb/library/actors/interconnect/ut_huge_cluster HugeCluster.AllToAll
ydb/library/actors/interconnect/ut_huge_cluster sole chunk chunk
+ydb/library/yaml_config/ut_transform test_transform.py.TestYamlConfigTransformations.test_basic[args1-dump_ds_init]
ydb/library/yql/dq/opt/ut HypergraphBuild.JoinTopologiesBenchmark
ydb/library/yql/dq/opt/ut sole chunk chunk
ydb/library/yql/providers/generic/connector/tests/datasource/ydb test.py.test_select_positive[column_selection_col2_COL1-kqprun]
@@ -179,8 +180,8 @@ ydb/tests/olap/ttl_tiering data_correctness.py.TestDataCorrectness.test
ydb/tests/olap/ttl_tiering data_migration_when_alter_ttl.py.TestDataMigrationWhenAlterTtl.test
ydb/tests/olap/ttl_tiering sole chunk chunk
ydb/tests/olap/ttl_tiering ttl_delete_s3.py.TestDeleteS3Ttl.test_data_unchanged_after_ttl_change
-ydb/tests/olap/ttl_tiering ttl_delete_s3.py.TestDeleteS3Ttl.test_ttl_delete
ydb/tests/olap/ttl_tiering ttl_delete_s3.py.TestDeleteS3Ttl.test_delete_s3_tiering
+ydb/tests/olap/ttl_tiering ttl_delete_s3.py.TestDeleteS3Ttl.test_ttl_delete
ydb/tests/olap/ttl_tiering ttl_unavailable_s3.py.TestUnavailableS3.test
ydb/tests/olap/ttl_tiering unstable_connection.py.TestUnstableConnection.test
ydb/tests/postgres_integrations/go-libpq [docker_wrapper_test.py] chunk chunk
diff --git a/build/conf/docs.conf b/build/conf/docs.conf
index 28d0cf0d7c..c0db3f454e 100644
--- a/build/conf/docs.conf
+++ b/build/conf/docs.conf
@@ -53,7 +53,8 @@ _DOCS_YFM_CMD=$_DOCS_YFM_CMD_IMPL($_DOCS_CONFIG_VALUE EXTRA_INPUTS $_DOCS_EXTRA_
### should be defined here.
module _DOCS_BARE_UNIT: _BARE_UNIT {
.ALLOWED=DOCS_DIR DOCS_CONFIG DOCS_VARS
- .CMD=TOUCH_DOCS_MF
+ .CMD=$TOUCH_DOCS_MF
+ .STRUCT_CMD=yes
.FINAL_TARGET=no
.NODE_TYPE=Bundle
.PEERDIR_POLICY=as_include
@@ -82,7 +83,7 @@ _DOCS_LIBRARY_CMD=$_DOCS_LIBRARY_CMD_IMPL(SOURCES $_DOCS_SOURCES_VALUE INCLUDE_S
# tag:docs
module DOCS_LIBRARY: _DOCS_BARE_UNIT {
- .CMD=_DOCS_LIBRARY_CMD
+ .CMD=$_DOCS_LIBRARY_CMD
.ALIASES=SRCS=_DOCS_SRCS DOCS_DIR=_YFM_DOCS_DIR
.EPILOGUE=_DOCS_LIBRARY_EPILOGUE
.NODE_TYPE=Library
@@ -150,7 +151,7 @@ macro _DOCS_YFM_USE_PLANTUML() {
### @see: [DOCS_DIR()](#macro_DOCS_DIR), [DOCS_CONFIG()](#macro_DOCS_CONFIG), [DOCS_VARS()](#macro_DOCS_VARS).
multimodule DOCS {
module DOCSBOOK: _DOCS_BASE_UNIT {
- .CMD=_DOCS_YFM_CMD
+ .CMD=$_DOCS_YFM_CMD
.FINAL_TARGET=yes
.PEERDIR_POLICY=as_build_from
.IGNORED=DOCS_DIR DOCS_INCLUDE_SOURCES DOCS_COPY_FILES PEERDIR PYTHON RUN_PROGRAM RUN_PYTHON3 RUN_LUA RUN_JAVA_PROGRAM FROM_SANDBOX SRCS COPY COPY_FILE FILES
@@ -167,7 +168,7 @@ multimodule DOCS {
}
module DOCSLIB: _DOCS_BASE_UNIT {
- .CMD=_DOCS_YFM_CMD
+ .CMD=$_DOCS_YFM_CMD
.PEERDIR_POLICY=as_build_from
.IGNORED=DOCS_DIR DOCS_INCLUDE_SOURCES DOCS_COPY_FILES PEERDIR PYTHON RUN_PROGRAM RUN_PYTHON3 RUN_LUA RUN_JAVA_PROGRAM FROM_SANDBOX SRCS COPY COPY_FILE FILES
.PEERDIRSELF=DOCSLIB_INTERNAL
diff --git a/build/conf/go.conf b/build/conf/go.conf
index 83b03e753c..9776f39287 100644
--- a/build/conf/go.conf
+++ b/build/conf/go.conf
@@ -1022,5 +1022,5 @@ macro GO_MOCKGEN_MOCKS() {
PEERDIR(vendor/go.uber.org/mock/gomock)
# Unfortunately ${rootrel;tool:TOOL} doesn't work currently, so we use this ugly workaround $MODDIR/gen/gen$_GO_EXE_SUFFIX
- RUN_PROGRAM(vendor/go.uber.org/mock/mockgen -package mocks -exec_only $MODDIR/gen/gen$_GO_EXE_SUFFIX ${GO_ARCADIA_PROJECT_PREFIX}${MOCKGEN_FROM} $MOCKGEN_TYPES STDOUT main.go TOOL $MODDIR/gen CWD $ARCADIA_BUILD_ROOT)
+ RUN_PROGRAM(vendor/go.uber.org/mock/mockgen -package mocks -exec_only $MODDIR/gen/gen$_GO_EXE_SUFFIX ${GO_ARCADIA_PROJECT_PREFIX}${MOCKGEN_FROM} $MOCKGEN_TYPES STDOUT main.go TOOL $MODDIR/gen CWD $ARCADIA_BUILD_ROOT ENV PATH=${GO_TOOLS_ROOT}/bin GOROOT=${GO_TOOLS_ROOT} GOCACHE=${BINDIR}/.gocache)
}
diff --git a/build/conf/java.conf b/build/conf/java.conf
index dbc53bb371..f76b0cfc90 100644
--- a/build/conf/java.conf
+++ b/build/conf/java.conf
@@ -451,11 +451,6 @@ macro DEFAULT_JUNIT_JAVA_SRCS_LAYOUT() {
FULL_JAVA_SRCS(RESOURCES SRCDIR resources **/* SKIP_CHECK_SRCDIR)
}
-macro _HASH_HELPER(Args...) {
- .CMD=${hash:Args}
- .SEM=${hash:Args}
-}
-
macro _GENTAR_HELPER(HASH_SUF="hash_suf", OUT_DIR[]) {
.CMD=${cwd:BINDIR} $YMAKE_PYTHON ${input:"build/scripts/autotar_gendirs.py"} --pack ${OUT_DIR} --outs ${output;tared;suf=$HASH_SUF:OUT_DIR} ${hide;kv:"tared_kind nodir"}
.SEM=${hide;suf=$HASH_SUF;tared;output:OUT_DIR}
diff --git a/build/conf/opensource.conf b/build/conf/opensource.conf
index ee6dd6cb74..067aacf3cb 100644
--- a/build/conf/opensource.conf
+++ b/build/conf/opensource.conf
@@ -41,6 +41,9 @@ when ($OPENSOURCE == "yes" && $EXPORT_GRADLE == "yes") {
EXPORT_SEM=yes
EXPORTED_BUILD_SYSTEM_SOURCE_ROOT=${"$"}{PROJECT_SOURCE_DIR}
EXPORTED_BUILD_SYSTEM_BUILD_ROOT=${"$"}{PROJECT_BINARY_DIR}
+}
+
+when ($EXPORT_GRADLE == "yes") {
EXPORT_LANG=JAVA
}
@@ -58,6 +61,9 @@ when ($OPENSOURCE == "yes" && $EXPORT_CMAKE == "yes") {
EXPORT_SEM=yes
EXPORTED_BUILD_SYSTEM_SOURCE_ROOT="${PROJECT_SOURCE_DIR}"
EXPORTED_BUILD_SYSTEM_BUILD_ROOT="${PROJECT_BINARY_DIR}"
+}
+
+when ($EXPORT_CMAKE == "yes") {
EXPORT_LANG=CPP
}
diff --git a/build/conf/python.conf b/build/conf/python.conf
index 8c0ca876a8..bfa42ee36d 100644
--- a/build/conf/python.conf
+++ b/build/conf/python.conf
@@ -267,6 +267,7 @@ multimodule PY3_PROGRAM {
# Notify pybuild to skip all python main function definitions
ENABLE(IGNORE_PY_MAIN)
NO_CLANG_TIDY()
+ DISABLE(START_TARGET)
}
}
@@ -435,6 +436,7 @@ multimodule PY3TEST {
PEERDIR+=library/python/pytest
_DONT_REQUIRE_LICENSE()
WITHOUT_VERSION()
+ DISABLE(START_TARGET)
}
}
diff --git a/build/conf/settings.conf b/build/conf/settings.conf
index 3f0ba1ef70..761d9d83af 100644
--- a/build/conf/settings.conf
+++ b/build/conf/settings.conf
@@ -1,19 +1,14 @@
JSON_CACHE_IS_ATTACHED=yes
LANGS_REQUIRE_BUILD_AND_SRC_ROOTS=c asm cython proto flatc swig ydl nlg
-CHECK_GO_INCORRECT_DEPS=yes
USE_PREBUILT_TOOLS=yes
RESOLVE_FORCE_LISTDIR=no
-ENABLE_NODE_SELF_UID=yes
REPORT_CONFIGURE_PROGRESS=yes
-FORCE_RESOLVE_MACRO_INCLS=yes
-ENABLE_RERESOLVE_FOR_GENERATED_FILES=yes
REPORT_ALL_DUPSRC=yes
DEPS_CACHE_CONTROL_UIDS_CACHE=yes
USE_GLOBAL_CMD=yes
PIC_NO_PIE=no
FAIL_PY2=no
MAIN_OUTPUT_AS_EXTRA=yes
-USE_REACHABILITY_TO_REPORT_CONF_ERRORS = yes
DISABLE_ATD=yes
when ($OS_WINDOWS == "yes") {
diff --git a/build/plugins/_dart_fields.py b/build/plugins/_dart_fields.py
index 8504891586..e102154a34 100644
--- a/build/plugins/_dart_fields.py
+++ b/build/plugins/_dart_fields.py
@@ -1137,6 +1137,20 @@ class TestFiles:
# https://a.yandex-team.ru/arcadia/devtools/ya/test/dartfile/__init__.py?rev=r14292146#L10
KEY2 = 'FILES'
+ _GRUT_PREFIX = 'grut'
+ _GRUT_INCLUDE_LINTER_TEST_PATHS = (
+ 'grut/libs/bigrt/clients',
+ 'grut/libs/bigrt/common',
+ 'grut/libs/bigrt/data',
+ 'grut/libs/bigrt/event_filter',
+ 'grut/libs/bigrt/info_keepers',
+ 'grut/libs/bigrt/processor',
+ 'grut/libs/bigrt/profile',
+ 'grut/libs/bigrt/profiles',
+ 'grut/libs/bigrt/queue_info_config',
+ 'grut/libs/shooter',
+ )
+
@classmethod
def value(cls, unit, flat_args, spec_args):
data_re = re.compile(r"sbr:/?/?(\d+)=?.*")
@@ -1230,6 +1244,13 @@ class TestFiles:
@classmethod
def cpp_linter_files(cls, unit, flat_args, spec_args):
+ upath = unit.path()[3:]
+ if upath.startswith(cls._GRUT_PREFIX):
+ for path in cls._GRUT_INCLUDE_LINTER_TEST_PATHS:
+ if os.path.commonpath([upath, path]) == path:
+ break
+ else:
+ raise DartValueError()
files_dart = _reference_group_var("ALL_SRCS", consts.STYLE_CPP_ALL_EXTS)
return {cls.KEY: files_dart, cls.KEY2: files_dart}
diff --git a/build/plugins/lib/test_const/__init__.py b/build/plugins/lib/test_const/__init__.py
index cd7534926c..293b514d01 100644
--- a/build/plugins/lib/test_const/__init__.py
+++ b/build/plugins/lib/test_const/__init__.py
@@ -181,6 +181,7 @@ NODEJS_RESOURCE = 'NODEJS_RESOURCE_GLOBAL'
NYC_RESOURCE = 'NYC_RESOURCE_GLOBAL'
RUFF_RESOURCE = 'RUFF_RESOURCE_GLOBAL'
CLANG_FORMAT_RESOURCE = 'CLANG_FORMAT_RESOURCE_GLOBAL'
+CLANG_FORMAT_15_RESOURCE = 'CLANG_FORMAT_15_RESOURCE_GLOBAL'
# test_tool resource for host platform.
# source - build/platform/test_tool/host.ya.make.inc.
diff --git a/build/plugins/ytest.py b/build/plugins/ytest.py
index 7adb582a91..4ae288e062 100644
--- a/build/plugins/ytest.py
+++ b/build/plugins/ytest.py
@@ -141,7 +141,18 @@ def validate_test(unit, kw):
if valid_kw.get('SCRIPT-REL-PATH') == 'boost.test':
project_path = valid_kw.get('BUILD-FOLDER-PATH', "")
if not project_path.startswith(
- ("contrib", "mail", "maps", "tools/idl", "metrika", "devtools", "mds", "yandex_io", "smart_devices")
+ (
+ "contrib",
+ "mail",
+ "maps",
+ "mobile/geo/maps",
+ "tools/idl",
+ "metrika",
+ "devtools",
+ "mds",
+ "yandex_io",
+ "smart_devices",
+ )
):
errors.append("BOOSTTEST is not allowed here")
diff --git a/build/scripts/fetch_resource.py b/build/scripts/fetch_resource.py
deleted file mode 100644
index d5af311e5d..0000000000
--- a/build/scripts/fetch_resource.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import urllib2
-import argparse
-import xmlrpclib
-
-
-def parse_args():
- parser = argparse.ArgumentParser()
- parser.add_argument('-r', '--resource-id', type=int, required=True)
- parser.add_argument('-o', '--output', required=True)
- return parser.parse_args()
-
-
-def fetch(url, retries=4, timeout=5):
- for i in xrange(retries):
- try:
- return urllib2.urlopen(url, timeout=timeout).read()
-
- except Exception:
- if i + 1 < retries:
- continue
-
- else:
- raise
-
-
-def fetch_resource(id_):
- urls = xmlrpclib.ServerProxy("https://sandbox.yandex-team.ru/sandbox/xmlrpc").get_resource_http_links(id_)
-
- for u in urls:
- try:
- return fetch(u)
-
- except Exception:
- continue
-
- raise Exception('Cannot fetch resource {}'.format(id_))
-
-
-if __name__ == '__main__':
- args = parse_args()
-
- with open(args.output, 'wb') as f:
- f.write(fetch_resource(int(args.resource_id)))
diff --git a/build/scripts/ya.make b/build/scripts/ya.make
index e59871172b..165a8da6db 100644
--- a/build/scripts/ya.make
+++ b/build/scripts/ya.make
@@ -16,7 +16,6 @@ IF (PY2)
fetch_from_archive.py
fetch_from_mds.py
fetch_from_sandbox.py
- fetch_resource.py
gen_java_codenav_entry.py
gen_py3_reg.py
go_tool.py
diff --git a/build/sysincl/emscripten.yml b/build/sysincl/emscripten.yml
index 2ac68cc16c..8c637abebe 100644
--- a/build/sysincl/emscripten.yml
+++ b/build/sysincl/emscripten.yml
@@ -108,6 +108,9 @@
- contrib/restricted/emscripten/system/lib/libc/musl/src/internal/syscall.h
- contrib/restricted/emscripten/system/lib/libc/musl/arch/emscripten/bits/syscall.h
+ - syscall_arch.h:
+ - contrib/restricted/emscripten/system/lib/libc/musl/arch/emscripten/syscall_arch.h
+
- inttypes.h: contrib/restricted/emscripten/system/lib/libc/musl/include/inttypes.h
- stddef.h: contrib/restricted/emscripten/system/lib/libc/musl/include/stddef.h
- stdint.h: contrib/restricted/emscripten/system/lib/libc/musl/include/stdint.h
diff --git a/build/ymake.core.conf b/build/ymake.core.conf
index 1ed20b6aab..b96a5a412b 100644
--- a/build/ymake.core.conf
+++ b/build/ymake.core.conf
@@ -487,8 +487,13 @@ macro CHECK_DEPENDENT_DIRS(TYPE, ALL?"UNUSED":"", PEERDIRS?"PEERDIRS":"ALL", RES
SET_APPEND(CHECK_DEPENDENT_DIRS_TYPES $TYPE)
}
+macro _HASH_HELPER(Args...) {
+ .CMD=${hash:Args}
+ .SEM=${hash:Args}
+}
+
macro _RESOURCE_SEM(INPUTS[], KEYS[], OPTS[]) {
- SET(RESOURCE_OUTPUT ${hash:INPUTS}.cpp)
+ SET(RESOURCE_OUTPUT $_HASH_HELPER($INPUTS $KEYS $OPTS).cpp)
.SEM=target_macroses-ITEM && target_macroses-macro resources && target_macroses-args ${output;global:RESOURCE_OUTPUT} INPUTS ${input:INPUTS} KEYS $KEYS OPTS $OPTS ${hide;tool:"tools/rescompiler/bin"}
}
@@ -775,9 +780,15 @@ module _BASE_UNIT: _BARE_UNIT {
when ($OS_EMSCRIPTEN == "yes") {
when ($NOLIBC != "yes") {
PEERDIR+=contrib/restricted/emscripten/include
+ # Important: In arcadia we usually build standalone WASM applications,
+ # so standalone library must go before lib/c in linker command-line.
+ # Emscripten uses two weak definitions for __clock_gettime function,
+ # so linker will pick one from the first library present in command
+ # line.
+ PEERDIR+=contrib/restricted/emscripten/system/lib/standalonewasm
+ PEERDIR+=contrib/restricted/emscripten/system/lib/stubs
PEERDIR+=contrib/restricted/emscripten/system/lib/c
PEERDIR+=contrib/restricted/emscripten/system/lib/dlmalloc
- PEERDIR+=contrib/restricted/emscripten/system/lib/standalonewasm
}
}
diff --git a/contrib/libs/cxxsupp/libcxxrt/.yandex_meta/override.nix b/contrib/libs/cxxsupp/libcxxrt/.yandex_meta/override.nix
index d80f39e895..af8de9dc63 100644
--- a/contrib/libs/cxxsupp/libcxxrt/.yandex_meta/override.nix
+++ b/contrib/libs/cxxsupp/libcxxrt/.yandex_meta/override.nix
@@ -1,12 +1,12 @@
pkgs: attrs: with pkgs; rec {
- version = "2024-10-14";
- revision = "76435c4451aeb5e04e9500b090293347a38cef8d";
+ version = "2025-02-25";
+ revision = "a6f71cbc3a1e1b8b9df241e081fa0ffdcde96249";
src = fetchFromGitHub {
owner = "libcxxrt";
repo = "libcxxrt";
rev = "${revision}";
- hash = "sha256-U7mq79/0xbyRr2+KUMKgEvyd2lfr3Q5GrByt/8J9sC8=";
+ hash = "sha256-+oTjU/DgOEIwJebSVkSEt22mJSdeONozB8FfzEiESHU=";
};
nativeBuildInputs = [ cmake ];
diff --git a/contrib/libs/cxxsupp/libcxxrt/exception.cc b/contrib/libs/cxxsupp/libcxxrt/exception.cc
index 088e62e449..49dbeff5d8 100644
--- a/contrib/libs/cxxsupp/libcxxrt/exception.cc
+++ b/contrib/libs/cxxsupp/libcxxrt/exception.cc
@@ -215,7 +215,7 @@ static_assert(offsetof(__cxa_dependent_exception, unwindHeader) ==
namespace std
{
- void unexpected();
+ [[noreturn]] void unexpected();
class exception
{
public:
@@ -287,12 +287,16 @@ namespace std
using namespace ABI_NAMESPACE;
+#ifdef LIBCXXRT_NO_DEFAULT_TERMINATE_DIAGNOSTICS
+/** The global termination handler. */
+static atomic<terminate_handler> terminateHandler = abort;
+#else
/**
* Callback function used with _Unwind_Backtrace().
*
* Prints a stack trace. Used only for debugging help.
*
- * Note: As of FreeBSD 8.1, dladd() still doesn't work properly, so this only
+ * Note: As of FreeBSD 8.1, dladdr() still doesn't work properly, so this only
* correctly prints function names from public, relocatable, symbols.
*/
static _Unwind_Reason_Code trace(struct _Unwind_Context *context, void *c)
@@ -313,24 +317,20 @@ static _Unwind_Reason_Code trace(struct _Unwind_Context *context, void *c)
}
static void terminate_with_diagnostics() {
- __cxa_eh_globals *globals = __cxa_get_globals();
- __cxa_exception *ex = globals->caughtExceptions;
+ __cxa_eh_globals *globals = __cxa_get_globals();
+ __cxa_exception *ex = globals->caughtExceptions;
- if (ex != nullptr) {
- fprintf(stderr, "uncaught exception:\n address -> %p\n", (void*)ex);
+ if (ex != nullptr) {
+ fprintf(stderr, "Terminating due to uncaught exception %p", static_cast<void*>(ex));
ex = realExceptionFromException(ex);
-
- const __class_type_info *e_ti =
+ const __class_type_info *e_ti =
static_cast<const __class_type_info*>(&typeid(std::exception));
- const __class_type_info *throw_ti =
+ const __class_type_info *throw_ti =
dynamic_cast<const __class_type_info*>(ex->exceptionType);
-
if (throw_ti) {
void* ptr = ex + 1;
-
if (throw_ti->__do_upcast(e_ti, &ptr)) {
std::exception* e = static_cast<std::exception*>(ptr);
-
if (e) {
fprintf(stderr, " what() -> \"%s\"\n", e->what());
}
@@ -342,14 +342,19 @@ static void terminate_with_diagnostics() {
const char *mangled = ex->exceptionType->name();
int status;
demangled = __cxa_demangle(mangled, demangled, &bufferSize, &status);
- fprintf(stderr, " type -> %s\n", status == 0 ? demangled : mangled);
+ fprintf(stderr, " of type %s\n", status == 0 ? demangled : mangled);
if (status == 0) { free(demangled); }
+
+ _Unwind_Backtrace(trace, 0);
}
- abort();
+
+ abort();
}
/** The global termination handler. */
static atomic<terminate_handler> terminateHandler = terminate_with_diagnostics;
+#endif
+
/** The global unexpected exception handler. */
static atomic<unexpected_handler> unexpectedHandler = std::terminate;
@@ -788,7 +793,8 @@ void __cxa_free_dependent_exception(void *thrown_exception)
* Report a failure that occurred when attempting to throw an exception.
*
* If the failure happened by falling off the end of the stack without finding
- * a handler, prints a back trace before aborting.
+ * a handler, catch the exception before calling terminate. The default
+ * terminate handler will print a backtrace before aborting.
*/
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
extern "C" void *__cxa_begin_catch(void *e) _LIBCXXRT_NOEXCEPT;
@@ -810,39 +816,6 @@ static void report_failure(_Unwind_Reason_Code err, __cxa_exception *thrown_exce
#endif
case _URC_END_OF_STACK:
__cxa_begin_catch (&(thrown_exception->unwindHeader));
- std::terminate();
- fprintf(stderr, "Terminating due to uncaught exception %p",
- static_cast<void*>(thrown_exception));
- thrown_exception = realExceptionFromException(thrown_exception);
- static const __class_type_info *e_ti =
- static_cast<const __class_type_info*>(&typeid(std::exception));
- const __class_type_info *throw_ti =
- dynamic_cast<const __class_type_info*>(thrown_exception->exceptionType);
- if (throw_ti)
- {
- std::exception *e =
- static_cast<std::exception*>(e_ti->cast_to(static_cast<void*>(thrown_exception+1),
- throw_ti));
- if (e)
- {
- fprintf(stderr, " '%s'", e->what());
- }
- }
-
- size_t bufferSize = 128;
- char *demangled = static_cast<char*>(malloc(bufferSize));
- const char *mangled = thrown_exception->exceptionType->name();
- int status;
- demangled = __cxa_demangle(mangled, demangled, &bufferSize, &status);
- fprintf(stderr, " of type %s\n",
- status == 0 ? demangled : mangled);
- if (status == 0) { free(demangled); }
- // Print a back trace if no handler is found.
- // TODO: Make this optional
- _Unwind_Backtrace(trace, 0);
-
- // Just abort. No need to call std::terminate for the second time
- abort();
break;
}
std::terminate();
@@ -1642,28 +1615,34 @@ namespace std
if (0 != info && 0 != info->terminateHandler)
{
info->terminateHandler();
- // Should not be reached - a terminate handler is not expected to
- // return.
- abort();
}
- terminateHandler.load()();
+ else
+ {
+ terminateHandler.load()();
+ }
+ // Should not be reached - a terminate handler is not expected
+ // to return.
+ abort();
}
/**
* Called when an unexpected exception is encountered (i.e. an exception
* violates an exception specification). This calls abort() unless a
* custom handler has been set..
*/
- void unexpected()
+ [[noreturn]] void unexpected()
{
static __cxa_thread_info *info = thread_info();
if (0 != info && 0 != info->unexpectedHandler)
{
info->unexpectedHandler();
- // Should not be reached - a terminate handler is not expected to
- // return.
- abort();
}
- unexpectedHandler.load()();
+ else
+ {
+ unexpectedHandler.load()();
+ }
+ // Should not be reached - a unexpected handler is not expected
+ // to return.
+ abort();
}
/**
* Returns whether there are any exceptions currently being thrown that
diff --git a/contrib/libs/cxxsupp/libcxxrt/patches/do-upcast-not-cast-to.patch b/contrib/libs/cxxsupp/libcxxrt/patches/do-upcast-not-cast-to.patch
new file mode 100644
index 0000000000..93fce488a5
--- /dev/null
+++ b/contrib/libs/cxxsupp/libcxxrt/patches/do-upcast-not-cast-to.patch
@@ -0,0 +1,17 @@
+--- contrib/libs/cxxsupp/libcxxrt/exception.cc (index)
++++ contrib/libs/cxxsupp/libcxxrt/exception.cc (working tree)
+@@ -321,7 +321,7 @@ static void terminate_with_diagnostics() {
+- if (throw_ti)
+- {
+- std::exception *e =
+- static_cast<std::exception*>(e_ti->cast_to(static_cast<void*>(ex+1), throw_ti));
+- if (e)
+- {
+- fprintf(stderr, " '%s'", e->what());
++ if (throw_ti) {
++ void* ptr = ex + 1;
++ if (throw_ti->__do_upcast(e_ti, &ptr)) {
++ std::exception* e = static_cast<std::exception*>(ptr);
++ if (e) {
++ fprintf(stderr, " what() -> \"%s\"\n", e->what());
++ }
diff --git a/contrib/libs/cxxsupp/libcxxrt/patches/pr41.1-move-trace.patch b/contrib/libs/cxxsupp/libcxxrt/patches/pr41.1-move-trace.patch
deleted file mode 100644
index b1331c9445..0000000000
--- a/contrib/libs/cxxsupp/libcxxrt/patches/pr41.1-move-trace.patch
+++ /dev/null
@@ -1,74 +0,0 @@
-commit 690915810d43430667f8d7b84fc7f88784f68fdf (HEAD -> update-libcxxrt)
-author: thegeorg
-date: 2025-02-26T13:56:00+03:00
-
- Revert "Simplify libcxxrt patches"
-
- This reverts commit 4358151e7723e37a39cf6f5478bfa336aa687fd4, reversing
- changes made to ad7618a2219d22bcd89a67fe082f290b4a9656ef.
-
---- contrib/libs/cxxsupp/libcxxrt/exception.cc (27ffe99a31d7fb0dd8a933c936f70853dac6041a)
-+++ contrib/libs/cxxsupp/libcxxrt/exception.cc (690915810d43430667f8d7b84fc7f88784f68fdf)
-@@ -287,6 +287,30 @@ namespace std
-
- using namespace ABI_NAMESPACE;
-
-+/**
-+ * Callback function used with _Unwind_Backtrace().
-+ *
-+ * Prints a stack trace. Used only for debugging help.
-+ *
-+ * Note: As of FreeBSD 8.1, dladd() still doesn't work properly, so this only
-+ * correctly prints function names from public, relocatable, symbols.
-+ */
-+static _Unwind_Reason_Code trace(struct _Unwind_Context *context, void *c)
-+{
-+ Dl_info myinfo;
-+ int mylookup =
-+ dladdr(reinterpret_cast<void *>(__cxa_current_exception_type), &myinfo);
-+ void *ip = reinterpret_cast<void*>(_Unwind_GetIP(context));
-+ Dl_info info;
-+ if (dladdr(ip, &info) != 0)
-+ {
-+ if (mylookup == 0 || strcmp(info.dli_fname, myinfo.dli_fname) != 0)
-+ {
-+ printf("%p:%s() in %s\n", ip, info.dli_sname, info.dli_fname);
-+ }
-+ }
-+ return _URC_CONTINUE_UNWIND;
-+}
-
-
- /** The global termination handler. */
-@@ -760,31 +785,6 @@ void __cxa_free_dependent_exception(void *thrown_exception)
- }
-
- /**
-- * Callback function used with _Unwind_Backtrace().
-- *
-- * Prints a stack trace. Used only for debugging help.
-- *
-- * Note: As of FreeBSD 8.1, dladd() still doesn't work properly, so this only
-- * correctly prints function names from public, relocatable, symbols.
-- */
--static _Unwind_Reason_Code trace(struct _Unwind_Context *context, void *c)
--{
-- Dl_info myinfo;
-- int mylookup =
-- dladdr(reinterpret_cast<void *>(__cxa_current_exception_type), &myinfo);
-- void *ip = reinterpret_cast<void*>(_Unwind_GetIP(context));
-- Dl_info info;
-- if (dladdr(ip, &info) != 0)
-- {
-- if (mylookup == 0 || strcmp(info.dli_fname, myinfo.dli_fname) != 0)
-- {
-- printf("%p:%s() in %s\n", ip, info.dli_sname, info.dli_fname);
-- }
-- }
-- return _URC_CONTINUE_UNWIND;
--}
--
--/**
- * Report a failure that occurred when attempting to throw an exception.
- *
- * If the failure happened by falling off the end of the stack without finding
diff --git a/contrib/libs/cxxsupp/libcxxrt/patches/pr41.2-fix-trace-format.patch b/contrib/libs/cxxsupp/libcxxrt/patches/pr41.2-fix-trace-format.patch
deleted file mode 100644
index a70c234b4f..0000000000
--- a/contrib/libs/cxxsupp/libcxxrt/patches/pr41.2-fix-trace-format.patch
+++ /dev/null
@@ -1,48 +0,0 @@
---- contrib/libs/cxxsupp/libcxxrt/exception.cc (index)
-+++ contrib/libs/cxxsupp/libcxxrt/exception.cc (working tree)
-@@ -312,9 +312,44 @@ static _Unwind_Reason_Code trace(struct _Unwind_Context *context, void *c)
- return _URC_CONTINUE_UNWIND;
- }
-
-+static void terminate_with_diagnostics() {
-+ __cxa_eh_globals *globals = __cxa_get_globals();
-+ __cxa_exception *ex = globals->caughtExceptions;
-+
-+ if (ex != nullptr) {
-+ fprintf(stderr, "uncaught exception:\n address -> %p\n", (void*)ex);
-+ ex = realExceptionFromException(ex);
-+
-+ const __class_type_info *e_ti =
-+ static_cast<const __class_type_info*>(&typeid(std::exception));
-+ const __class_type_info *throw_ti =
-+ dynamic_cast<const __class_type_info*>(ex->exceptionType);
-+
-+ if (throw_ti) {
-+ void* ptr = ex + 1;
-+
-+ if (throw_ti->__do_upcast(e_ti, &ptr)) {
-+ std::exception* e = static_cast<std::exception*>(ptr);
-+
-+ if (e) {
-+ fprintf(stderr, " what() -> \"%s\"\n", e->what());
-+ }
-+ }
-+ }
-+
-+ size_t bufferSize = 128;
-+ char *demangled = static_cast<char*>(malloc(bufferSize));
-+ const char *mangled = ex->exceptionType->name();
-+ int status;
-+ demangled = __cxa_demangle(mangled, demangled, &bufferSize, &status);
-+ fprintf(stderr, " type -> %s\n", status == 0 ? demangled : mangled);
-+ if (status == 0) { free(demangled); }
-+ }
-+ abort();
-+}
-
- /** The global termination handler. */
--static atomic<terminate_handler> terminateHandler = abort;
-+static atomic<terminate_handler> terminateHandler = terminate_with_diagnostics;
- /** The global unexpected exception handler. */
- static atomic<unexpected_handler> unexpectedHandler = std::terminate;
-
diff --git a/contrib/libs/cxxsupp/libcxxrt/patches/pr49-cosmetics.patch b/contrib/libs/cxxsupp/libcxxrt/patches/pr49-cosmetics.patch
new file mode 100644
index 0000000000..740aed0c11
--- /dev/null
+++ b/contrib/libs/cxxsupp/libcxxrt/patches/pr49-cosmetics.patch
@@ -0,0 +1,24 @@
+From 9a9bcd8ef3e635194f9bfaa1085b8ed2b88455e3 Mon Sep 17 00:00:00 2001
+From: Yuriy Chernyshov <thegeorg@yandex-team.com>
+Date: Thu, 27 Feb 2025 16:38:01 +0100
+Subject: [PATCH] Merge short lines into one
+
+We still need to reduce the diff with our downstream, as #41 does not work for us out of the box.
+---
+ src/exception.cc | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/src/exception.cc b/src/exception.cc
+index 9bcf903..ccf32de 100644
+--- a/exception.cc
++++ b/exception.cc
+@@ -356,8 +356,7 @@ static void terminate_with_diagnostics() {
+ const char *mangled = ex->exceptionType->name();
+ int status;
+ demangled = __cxa_demangle(mangled, demangled, &bufferSize, &status);
+- fprintf(stderr, " of type %s\n",
+- status == 0 ? demangled : mangled);
++ fprintf(stderr, " of type %s\n", status == 0 ? demangled : mangled);
+ if (status == 0) { free(demangled); }
+
+ _Unwind_Backtrace(trace, 0);
diff --git a/contrib/libs/cxxsupp/libcxxrt/ya.make b/contrib/libs/cxxsupp/libcxxrt/ya.make
index f9eb8743d8..f9b1964b1f 100644
--- a/contrib/libs/cxxsupp/libcxxrt/ya.make
+++ b/contrib/libs/cxxsupp/libcxxrt/ya.make
@@ -11,9 +11,9 @@ LICENSE(
LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
-VERSION(2024-10-14)
+VERSION(2025-02-25)
-ORIGINAL_SOURCE(https://github.com/libcxxrt/libcxxrt/archive/76435c4451aeb5e04e9500b090293347a38cef8d.tar.gz)
+ORIGINAL_SOURCE(https://github.com/libcxxrt/libcxxrt/archive/a6f71cbc3a1e1b8b9df241e081fa0ffdcde96249.tar.gz)
PEERDIR(
contrib/libs/libunwind
diff --git a/contrib/libs/llvm14/lib/CodeGen/MIRParser/ya.make b/contrib/libs/llvm14/lib/CodeGen/MIRParser/ya.make
index 1cc779f6a6..b946280f8b 100644
--- a/contrib/libs/llvm14/lib/CodeGen/MIRParser/ya.make
+++ b/contrib/libs/llvm14/lib/CodeGen/MIRParser/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/DWARFLinker/ya.make b/contrib/libs/llvm14/lib/DWARFLinker/ya.make
index 1c8ae4a195..8c5d3ae624 100644
--- a/contrib/libs/llvm14/lib/DWARFLinker/ya.make
+++ b/contrib/libs/llvm14/lib/DWARFLinker/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/DWP/ya.make b/contrib/libs/llvm14/lib/DWP/ya.make
index 4c42a8d7d8..013db9379a 100644
--- a/contrib/libs/llvm14/lib/DWP/ya.make
+++ b/contrib/libs/llvm14/lib/DWP/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/DebugInfo/GSYM/ya.make b/contrib/libs/llvm14/lib/DebugInfo/GSYM/ya.make
index 71b4a338a4..f62cf1c9be 100644
--- a/contrib/libs/llvm14/lib/DebugInfo/GSYM/ya.make
+++ b/contrib/libs/llvm14/lib/DebugInfo/GSYM/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/DebugInfo/PDB/ya.make b/contrib/libs/llvm14/lib/DebugInfo/PDB/ya.make
index e65f185591..efd2086347 100644
--- a/contrib/libs/llvm14/lib/DebugInfo/PDB/ya.make
+++ b/contrib/libs/llvm14/lib/DebugInfo/PDB/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/DebugInfo/Symbolize/ya.make b/contrib/libs/llvm14/lib/DebugInfo/Symbolize/ya.make
index 1e9ece62ec..e8f8472996 100644
--- a/contrib/libs/llvm14/lib/DebugInfo/Symbolize/ya.make
+++ b/contrib/libs/llvm14/lib/DebugInfo/Symbolize/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/ExecutionEngine/Interpreter/ya.make b/contrib/libs/llvm14/lib/ExecutionEngine/Interpreter/ya.make
index cf474449bf..2dcc00b4e9 100644
--- a/contrib/libs/llvm14/lib/ExecutionEngine/Interpreter/ya.make
+++ b/contrib/libs/llvm14/lib/ExecutionEngine/Interpreter/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/ExecutionEngine/JITLink/ya.make b/contrib/libs/llvm14/lib/ExecutionEngine/JITLink/ya.make
index e1844bf4e7..f382e26e31 100644
--- a/contrib/libs/llvm14/lib/ExecutionEngine/JITLink/ya.make
+++ b/contrib/libs/llvm14/lib/ExecutionEngine/JITLink/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(
diff --git a/contrib/libs/llvm14/lib/ExecutionEngine/Orc/ya.make b/contrib/libs/llvm14/lib/ExecutionEngine/Orc/ya.make
index 64d9456302..062953b4fb 100644
--- a/contrib/libs/llvm14/lib/ExecutionEngine/Orc/ya.make
+++ b/contrib/libs/llvm14/lib/ExecutionEngine/Orc/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Extensions/ya.make b/contrib/libs/llvm14/lib/Extensions/ya.make
index 87ebf15124..5a55406f1a 100644
--- a/contrib/libs/llvm14/lib/Extensions/ya.make
+++ b/contrib/libs/llvm14/lib/Extensions/ya.make
@@ -4,6 +4,8 @@ LIBRARY()
WITHOUT_LICENSE_TEXTS()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(NCSA)
diff --git a/contrib/libs/llvm14/lib/FileCheck/ya.make b/contrib/libs/llvm14/lib/FileCheck/ya.make
index 3c23b3ec2e..1cc8e15030 100644
--- a/contrib/libs/llvm14/lib/FileCheck/ya.make
+++ b/contrib/libs/llvm14/lib/FileCheck/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Frontend/OpenACC/ya.make b/contrib/libs/llvm14/lib/Frontend/OpenACC/ya.make
index 8bcda0013e..7632111f27 100644
--- a/contrib/libs/llvm14/lib/Frontend/OpenACC/ya.make
+++ b/contrib/libs/llvm14/lib/Frontend/OpenACC/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/FuzzMutate/ya.make b/contrib/libs/llvm14/lib/FuzzMutate/ya.make
index 3b5aa6acc4..e0abb51d03 100644
--- a/contrib/libs/llvm14/lib/FuzzMutate/ya.make
+++ b/contrib/libs/llvm14/lib/FuzzMutate/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/InterfaceStub/ya.make b/contrib/libs/llvm14/lib/InterfaceStub/ya.make
index 3e94c6c1dc..53ede22468 100644
--- a/contrib/libs/llvm14/lib/InterfaceStub/ya.make
+++ b/contrib/libs/llvm14/lib/InterfaceStub/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/LTO/ya.make b/contrib/libs/llvm14/lib/LTO/ya.make
index c9329a9e53..0175c3eec4 100644
--- a/contrib/libs/llvm14/lib/LTO/ya.make
+++ b/contrib/libs/llvm14/lib/LTO/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/LineEditor/ya.make b/contrib/libs/llvm14/lib/LineEditor/ya.make
index 9eb4ef0191..f3325b9427 100644
--- a/contrib/libs/llvm14/lib/LineEditor/ya.make
+++ b/contrib/libs/llvm14/lib/LineEditor/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/MCA/ya.make b/contrib/libs/llvm14/lib/MCA/ya.make
index bfb93027dc..93678d8535 100644
--- a/contrib/libs/llvm14/lib/MCA/ya.make
+++ b/contrib/libs/llvm14/lib/MCA/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/ObjectYAML/ya.make b/contrib/libs/llvm14/lib/ObjectYAML/ya.make
index 7f6a7c0a03..94cbbc66f5 100644
--- a/contrib/libs/llvm14/lib/ObjectYAML/ya.make
+++ b/contrib/libs/llvm14/lib/ObjectYAML/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Option/ya.make b/contrib/libs/llvm14/lib/Option/ya.make
index d981c842bb..71c473b2e5 100644
--- a/contrib/libs/llvm14/lib/Option/ya.make
+++ b/contrib/libs/llvm14/lib/Option/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Passes/ya.make b/contrib/libs/llvm14/lib/Passes/ya.make
index 195eb5f388..d5598b9329 100644
--- a/contrib/libs/llvm14/lib/Passes/ya.make
+++ b/contrib/libs/llvm14/lib/Passes/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/ProfileData/Coverage/ya.make b/contrib/libs/llvm14/lib/ProfileData/Coverage/ya.make
index 72bc2af83d..002c263931 100644
--- a/contrib/libs/llvm14/lib/ProfileData/Coverage/ya.make
+++ b/contrib/libs/llvm14/lib/ProfileData/Coverage/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/AArch64/AsmParser/ya.make b/contrib/libs/llvm14/lib/Target/AArch64/AsmParser/ya.make
index 86b0033a93..b4e2198098 100644
--- a/contrib/libs/llvm14/lib/Target/AArch64/AsmParser/ya.make
+++ b/contrib/libs/llvm14/lib/Target/AArch64/AsmParser/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/AArch64/Disassembler/ya.make b/contrib/libs/llvm14/lib/Target/AArch64/Disassembler/ya.make
index 0153cb900a..b5c1b176e2 100644
--- a/contrib/libs/llvm14/lib/Target/AArch64/Disassembler/ya.make
+++ b/contrib/libs/llvm14/lib/Target/AArch64/Disassembler/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc/ya.make b/contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc/ya.make
index 3996127ec9..5774bc4bc2 100644
--- a/contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc/ya.make
+++ b/contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/AArch64/TargetInfo/ya.make b/contrib/libs/llvm14/lib/Target/AArch64/TargetInfo/ya.make
index 33f279fb20..321ca1c837 100644
--- a/contrib/libs/llvm14/lib/Target/AArch64/TargetInfo/ya.make
+++ b/contrib/libs/llvm14/lib/Target/AArch64/TargetInfo/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/AArch64/Utils/ya.make b/contrib/libs/llvm14/lib/Target/AArch64/Utils/ya.make
index 86a6860e41..472e3f3fdd 100644
--- a/contrib/libs/llvm14/lib/Target/AArch64/Utils/ya.make
+++ b/contrib/libs/llvm14/lib/Target/AArch64/Utils/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/AArch64/ya.make b/contrib/libs/llvm14/lib/Target/AArch64/ya.make
index a773c70646..dcaa68a65d 100644
--- a/contrib/libs/llvm14/lib/Target/AArch64/ya.make
+++ b/contrib/libs/llvm14/lib/Target/AArch64/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/ARM/AsmParser/ya.make b/contrib/libs/llvm14/lib/Target/ARM/AsmParser/ya.make
index f66c2b4b0f..74d91274cc 100644
--- a/contrib/libs/llvm14/lib/Target/ARM/AsmParser/ya.make
+++ b/contrib/libs/llvm14/lib/Target/ARM/AsmParser/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/ARM/Disassembler/ya.make b/contrib/libs/llvm14/lib/Target/ARM/Disassembler/ya.make
index a66db9c90f..3ef6e75115 100644
--- a/contrib/libs/llvm14/lib/Target/ARM/Disassembler/ya.make
+++ b/contrib/libs/llvm14/lib/Target/ARM/Disassembler/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc/ya.make b/contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc/ya.make
index 53be40e3a6..4c4d22f68c 100644
--- a/contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc/ya.make
+++ b/contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/ARM/TargetInfo/ya.make b/contrib/libs/llvm14/lib/Target/ARM/TargetInfo/ya.make
index c63a083fb5..53f5d6c169 100644
--- a/contrib/libs/llvm14/lib/Target/ARM/TargetInfo/ya.make
+++ b/contrib/libs/llvm14/lib/Target/ARM/TargetInfo/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/ARM/Utils/ya.make b/contrib/libs/llvm14/lib/Target/ARM/Utils/ya.make
index 74bef2cc19..876f51dd82 100644
--- a/contrib/libs/llvm14/lib/Target/ARM/Utils/ya.make
+++ b/contrib/libs/llvm14/lib/Target/ARM/Utils/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/ARM/ya.make b/contrib/libs/llvm14/lib/Target/ARM/ya.make
index 39a7c2e9c4..b48e06a152 100644
--- a/contrib/libs/llvm14/lib/Target/ARM/ya.make
+++ b/contrib/libs/llvm14/lib/Target/ARM/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/BPF/AsmParser/ya.make b/contrib/libs/llvm14/lib/Target/BPF/AsmParser/ya.make
index a02ef07bc6..65636826d5 100644
--- a/contrib/libs/llvm14/lib/Target/BPF/AsmParser/ya.make
+++ b/contrib/libs/llvm14/lib/Target/BPF/AsmParser/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/BPF/Disassembler/ya.make b/contrib/libs/llvm14/lib/Target/BPF/Disassembler/ya.make
index 284989d3ea..7306e77e8b 100644
--- a/contrib/libs/llvm14/lib/Target/BPF/Disassembler/ya.make
+++ b/contrib/libs/llvm14/lib/Target/BPF/Disassembler/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc/ya.make b/contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc/ya.make
index d9964ea5f8..cd189ea70d 100644
--- a/contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc/ya.make
+++ b/contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/BPF/TargetInfo/ya.make b/contrib/libs/llvm14/lib/Target/BPF/TargetInfo/ya.make
index a0aeeff1a3..83678a4b99 100644
--- a/contrib/libs/llvm14/lib/Target/BPF/TargetInfo/ya.make
+++ b/contrib/libs/llvm14/lib/Target/BPF/TargetInfo/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/BPF/ya.make b/contrib/libs/llvm14/lib/Target/BPF/ya.make
index 59fd10bf60..72ed0c56d0 100644
--- a/contrib/libs/llvm14/lib/Target/BPF/ya.make
+++ b/contrib/libs/llvm14/lib/Target/BPF/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc/ya.make b/contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc/ya.make
index 4fac6e4b95..71adacfd36 100644
--- a/contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc/ya.make
+++ b/contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo/ya.make b/contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo/ya.make
index 912c02bbb8..0db3b7c935 100644
--- a/contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo/ya.make
+++ b/contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/NVPTX/ya.make b/contrib/libs/llvm14/lib/Target/NVPTX/ya.make
index b2ade7229f..0a7e509514 100644
--- a/contrib/libs/llvm14/lib/Target/NVPTX/ya.make
+++ b/contrib/libs/llvm14/lib/Target/NVPTX/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/PowerPC/AsmParser/ya.make b/contrib/libs/llvm14/lib/Target/PowerPC/AsmParser/ya.make
index a24a8f8ec1..13882a7b74 100644
--- a/contrib/libs/llvm14/lib/Target/PowerPC/AsmParser/ya.make
+++ b/contrib/libs/llvm14/lib/Target/PowerPC/AsmParser/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/PowerPC/Disassembler/ya.make b/contrib/libs/llvm14/lib/Target/PowerPC/Disassembler/ya.make
index 95c0eeeff7..e2a6fd59a6 100644
--- a/contrib/libs/llvm14/lib/Target/PowerPC/Disassembler/ya.make
+++ b/contrib/libs/llvm14/lib/Target/PowerPC/Disassembler/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc/ya.make b/contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc/ya.make
index 17d422af3d..a2c1a0b658 100644
--- a/contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc/ya.make
+++ b/contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo/ya.make b/contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo/ya.make
index b035521fd7..8e7c500147 100644
--- a/contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo/ya.make
+++ b/contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Target/PowerPC/ya.make b/contrib/libs/llvm14/lib/Target/PowerPC/ya.make
index 347a8b1c3f..8ff7a52ea4 100644
--- a/contrib/libs/llvm14/lib/Target/PowerPC/ya.make
+++ b/contrib/libs/llvm14/lib/Target/PowerPC/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/ToolDrivers/llvm-dlltool/ya.make b/contrib/libs/llvm14/lib/ToolDrivers/llvm-dlltool/ya.make
index fa7ac3830d..47d9c0ef26 100644
--- a/contrib/libs/llvm14/lib/ToolDrivers/llvm-dlltool/ya.make
+++ b/contrib/libs/llvm14/lib/ToolDrivers/llvm-dlltool/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/ToolDrivers/llvm-lib/ya.make b/contrib/libs/llvm14/lib/ToolDrivers/llvm-lib/ya.make
index 7bf1853d0b..da2b07fa19 100644
--- a/contrib/libs/llvm14/lib/ToolDrivers/llvm-lib/ya.make
+++ b/contrib/libs/llvm14/lib/ToolDrivers/llvm-lib/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/Transforms/Coroutines/ya.make b/contrib/libs/llvm14/lib/Transforms/Coroutines/ya.make
index 1359dd6df0..807a94985a 100644
--- a/contrib/libs/llvm14/lib/Transforms/Coroutines/ya.make
+++ b/contrib/libs/llvm14/lib/Transforms/Coroutines/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/WindowsManifest/ya.make b/contrib/libs/llvm14/lib/WindowsManifest/ya.make
index 08c6ad8e33..9212603975 100644
--- a/contrib/libs/llvm14/lib/WindowsManifest/ya.make
+++ b/contrib/libs/llvm14/lib/WindowsManifest/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/lib/XRay/ya.make b/contrib/libs/llvm14/lib/XRay/ya.make
index 451ce68256..5b3919203c 100644
--- a/contrib/libs/llvm14/lib/XRay/ya.make
+++ b/contrib/libs/llvm14/lib/XRay/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/bugpoint/ya.make b/contrib/libs/llvm14/tools/bugpoint/ya.make
index b6ccc9cc6c..2c92dc3f78 100644
--- a/contrib/libs/llvm14/tools/bugpoint/ya.make
+++ b/contrib/libs/llvm14/tools/bugpoint/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/dsymutil/ya.make b/contrib/libs/llvm14/tools/dsymutil/ya.make
index e5b28b06d4..1fd7a3794b 100644
--- a/contrib/libs/llvm14/tools/dsymutil/ya.make
+++ b/contrib/libs/llvm14/tools/dsymutil/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/gold/ya.make b/contrib/libs/llvm14/tools/gold/ya.make
index 78e06212fc..a1df0fa646 100644
--- a/contrib/libs/llvm14/tools/gold/ya.make
+++ b/contrib/libs/llvm14/tools/gold/ya.make
@@ -2,6 +2,8 @@
DLL(LLVMgold PREFIX "")
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llc/ya.make b/contrib/libs/llvm14/tools/llc/ya.make
index e8afa15539..57c55653ca 100644
--- a/contrib/libs/llvm14/tools/llc/ya.make
+++ b/contrib/libs/llvm14/tools/llc/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/lli/ChildTarget/ya.make b/contrib/libs/llvm14/tools/lli/ChildTarget/ya.make
index 3418e04553..13086fcf46 100644
--- a/contrib/libs/llvm14/tools/lli/ChildTarget/ya.make
+++ b/contrib/libs/llvm14/tools/lli/ChildTarget/ya.make
@@ -2,6 +2,8 @@
PROGRAM(lli-child-target)
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/lli/ya.make b/contrib/libs/llvm14/tools/lli/ya.make
index dc56e17046..8b57788f71 100644
--- a/contrib/libs/llvm14/tools/lli/ya.make
+++ b/contrib/libs/llvm14/tools/lli/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-ar/ya.make b/contrib/libs/llvm14/tools/llvm-ar/ya.make
index 1ce94bcb6f..28c9072a27 100644
--- a/contrib/libs/llvm14/tools/llvm-ar/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-ar/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-as/ya.make b/contrib/libs/llvm14/tools/llvm-as/ya.make
index 48d5e6e683..28577ac80b 100644
--- a/contrib/libs/llvm14/tools/llvm-as/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-as/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-bcanalyzer/ya.make b/contrib/libs/llvm14/tools/llvm-bcanalyzer/ya.make
index 3d57161d8d..0734332b00 100644
--- a/contrib/libs/llvm14/tools/llvm-bcanalyzer/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-bcanalyzer/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-cat/ya.make b/contrib/libs/llvm14/tools/llvm-cat/ya.make
index 2101c805ee..9e2b10ff9d 100644
--- a/contrib/libs/llvm14/tools/llvm-cat/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-cat/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/ya.make b/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/ya.make
index d3b196a413..ae93f02c0d 100644
--- a/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-cfi-verify/ya.make b/contrib/libs/llvm14/tools/llvm-cfi-verify/ya.make
index cef9ba064d..b42fbc18e0 100644
--- a/contrib/libs/llvm14/tools/llvm-cfi-verify/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-cfi-verify/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-config/ya.make b/contrib/libs/llvm14/tools/llvm-config/ya.make
index 81523992ae..1162e8d0d2 100644
--- a/contrib/libs/llvm14/tools/llvm-config/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-config/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-cov/ya.make b/contrib/libs/llvm14/tools/llvm-cov/ya.make
index 9bc4c3debe..bbdd860fd3 100644
--- a/contrib/libs/llvm14/tools/llvm-cov/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-cov/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-cvtres/ya.make b/contrib/libs/llvm14/tools/llvm-cvtres/ya.make
index 706e0a9876..c0a91d3a0a 100644
--- a/contrib/libs/llvm14/tools/llvm-cvtres/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-cvtres/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-cxxdump/ya.make b/contrib/libs/llvm14/tools/llvm-cxxdump/ya.make
index 00af0e4b02..cd69d47245 100644
--- a/contrib/libs/llvm14/tools/llvm-cxxdump/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-cxxdump/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-cxxfilt/ya.make b/contrib/libs/llvm14/tools/llvm-cxxfilt/ya.make
index 77f0e4f186..5d36a25c2a 100644
--- a/contrib/libs/llvm14/tools/llvm-cxxfilt/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-cxxfilt/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-cxxmap/ya.make b/contrib/libs/llvm14/tools/llvm-cxxmap/ya.make
index 4c05475986..1761466b26 100644
--- a/contrib/libs/llvm14/tools/llvm-cxxmap/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-cxxmap/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-diff/lib/ya.make b/contrib/libs/llvm14/tools/llvm-diff/lib/ya.make
index 4dd6acd33b..36fd72939d 100644
--- a/contrib/libs/llvm14/tools/llvm-diff/lib/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-diff/lib/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-diff/ya.make b/contrib/libs/llvm14/tools/llvm-diff/ya.make
index 86189b0ff3..f0f3f8bd8a 100644
--- a/contrib/libs/llvm14/tools/llvm-diff/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-diff/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-dis/ya.make b/contrib/libs/llvm14/tools/llvm-dis/ya.make
index 46629d7e61..33d964c4a6 100644
--- a/contrib/libs/llvm14/tools/llvm-dis/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-dis/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-dwarfdump/ya.make b/contrib/libs/llvm14/tools/llvm-dwarfdump/ya.make
index 924e23a023..680a091c45 100644
--- a/contrib/libs/llvm14/tools/llvm-dwarfdump/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-dwarfdump/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-dwp/ya.make b/contrib/libs/llvm14/tools/llvm-dwp/ya.make
index 68226e677b..6ffcf3cec9 100644
--- a/contrib/libs/llvm14/tools/llvm-dwp/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-dwp/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/ya.make b/contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/ya.make
index cc0d66211f..bbe60d6281 100644
--- a/contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/ya.make b/contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/ya.make
index 953fd9824c..1928fc0f8d 100644
--- a/contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/ya.make b/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/ya.make
index 88c500fa8f..568158046d 100644
--- a/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/ya.make b/contrib/libs/llvm14/tools/llvm-exegesis/lib/ya.make
index 6b535feb96..61083d3dde 100644
--- a/contrib/libs/llvm14/tools/llvm-exegesis/lib/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/ya.make b/contrib/libs/llvm14/tools/llvm-exegesis/ya.make
index 6e9251c81f..afdb24ff6a 100644
--- a/contrib/libs/llvm14/tools/llvm-exegesis/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-extract/ya.make b/contrib/libs/llvm14/tools/llvm-extract/ya.make
index 619d1cce51..20eac43f24 100644
--- a/contrib/libs/llvm14/tools/llvm-extract/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-extract/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-gsymutil/ya.make b/contrib/libs/llvm14/tools/llvm-gsymutil/ya.make
index ea67d249d4..ed0d20db16 100644
--- a/contrib/libs/llvm14/tools/llvm-gsymutil/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-gsymutil/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-ifs/ya.make b/contrib/libs/llvm14/tools/llvm-ifs/ya.make
index a1b79aab42..1fb1e6e5c6 100644
--- a/contrib/libs/llvm14/tools/llvm-ifs/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-ifs/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/ya.make b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/ya.make
index dd0107ba99..07bba6dc4a 100644
--- a/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-jitlink/ya.make b/contrib/libs/llvm14/tools/llvm-jitlink/ya.make
index 132e84dfd0..1d9421c566 100644
--- a/contrib/libs/llvm14/tools/llvm-jitlink/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-jitlink/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-libtool-darwin/ya.make b/contrib/libs/llvm14/tools/llvm-libtool-darwin/ya.make
index 4cffcb76bc..99ad2efb1d 100644
--- a/contrib/libs/llvm14/tools/llvm-libtool-darwin/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-libtool-darwin/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-link/ya.make b/contrib/libs/llvm14/tools/llvm-link/ya.make
index de434f851c..1d7b183a51 100644
--- a/contrib/libs/llvm14/tools/llvm-link/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-link/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-lipo/ya.make b/contrib/libs/llvm14/tools/llvm-lipo/ya.make
index 97fd93bc16..3d987c814d 100644
--- a/contrib/libs/llvm14/tools/llvm-lipo/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-lipo/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-lto/ya.make b/contrib/libs/llvm14/tools/llvm-lto/ya.make
index b9d03c9218..1b3aac66f9 100644
--- a/contrib/libs/llvm14/tools/llvm-lto/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-lto/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-lto2/ya.make b/contrib/libs/llvm14/tools/llvm-lto2/ya.make
index 1869e4a344..51450ac778 100644
--- a/contrib/libs/llvm14/tools/llvm-lto2/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-lto2/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-mc/ya.make b/contrib/libs/llvm14/tools/llvm-mc/ya.make
index 0e71f18028..515e55259f 100644
--- a/contrib/libs/llvm14/tools/llvm-mc/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-mc/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-mca/ya.make b/contrib/libs/llvm14/tools/llvm-mca/ya.make
index 0346c0a5cc..df21987ed5 100644
--- a/contrib/libs/llvm14/tools/llvm-mca/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-mca/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-ml/ya.make b/contrib/libs/llvm14/tools/llvm-ml/ya.make
index 4c2e95de0d..718ed5f505 100644
--- a/contrib/libs/llvm14/tools/llvm-ml/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-ml/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-modextract/ya.make b/contrib/libs/llvm14/tools/llvm-modextract/ya.make
index 3e98583ba1..3ec9981389 100644
--- a/contrib/libs/llvm14/tools/llvm-modextract/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-modextract/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-mt/ya.make b/contrib/libs/llvm14/tools/llvm-mt/ya.make
index c10b978f5b..1b7f48ed79 100644
--- a/contrib/libs/llvm14/tools/llvm-mt/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-mt/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-nm/ya.make b/contrib/libs/llvm14/tools/llvm-nm/ya.make
index bb57fc4cf6..4ab4c7c0ed 100644
--- a/contrib/libs/llvm14/tools/llvm-nm/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-nm/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/ya.make b/contrib/libs/llvm14/tools/llvm-objcopy/ya.make
index 0be7feaf39..92e1cde2a2 100644
--- a/contrib/libs/llvm14/tools/llvm-objcopy/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/ya.make b/contrib/libs/llvm14/tools/llvm-objdump/ya.make
index d4ef19c193..b8da19c89b 100644
--- a/contrib/libs/llvm14/tools/llvm-objdump/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-objdump/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-opt-report/ya.make b/contrib/libs/llvm14/tools/llvm-opt-report/ya.make
index c747b91c42..b70342853e 100644
--- a/contrib/libs/llvm14/tools/llvm-opt-report/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-opt-report/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/ya.make b/contrib/libs/llvm14/tools/llvm-pdbutil/ya.make
index 2d8b043b40..6e10940bfb 100644
--- a/contrib/libs/llvm14/tools/llvm-pdbutil/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-profdata/ya.make b/contrib/libs/llvm14/tools/llvm-profdata/ya.make
index 628b163b23..64d113f31b 100644
--- a/contrib/libs/llvm14/tools/llvm-profdata/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-profdata/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/ya.make b/contrib/libs/llvm14/tools/llvm-profgen/ya.make
index f4518c571f..ef8a72c5e3 100644
--- a/contrib/libs/llvm14/tools/llvm-profgen/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-profgen/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ya.make b/contrib/libs/llvm14/tools/llvm-rc/ya.make
index 0734abe2dc..b757795675 100644
--- a/contrib/libs/llvm14/tools/llvm-rc/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-rc/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/ya.make b/contrib/libs/llvm14/tools/llvm-readobj/ya.make
index b09c1b9dae..59af4fb196 100644
--- a/contrib/libs/llvm14/tools/llvm-readobj/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-readobj/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/ya.make b/contrib/libs/llvm14/tools/llvm-reduce/ya.make
index 4dfeb1339d..f331598c08 100644
--- a/contrib/libs/llvm14/tools/llvm-reduce/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-reduce/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-rtdyld/ya.make b/contrib/libs/llvm14/tools/llvm-rtdyld/ya.make
index 06ab2d2c88..f163718ce8 100644
--- a/contrib/libs/llvm14/tools/llvm-rtdyld/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-rtdyld/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-size/ya.make b/contrib/libs/llvm14/tools/llvm-size/ya.make
index 88731a7476..60e3ed8e61 100644
--- a/contrib/libs/llvm14/tools/llvm-size/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-size/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-split/ya.make b/contrib/libs/llvm14/tools/llvm-split/ya.make
index 7099375aa9..2c028e36ba 100644
--- a/contrib/libs/llvm14/tools/llvm-split/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-split/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-stress/ya.make b/contrib/libs/llvm14/tools/llvm-stress/ya.make
index a6a10a3323..e8fdd4971a 100644
--- a/contrib/libs/llvm14/tools/llvm-stress/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-stress/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-strings/ya.make b/contrib/libs/llvm14/tools/llvm-strings/ya.make
index 85ae64afaf..5dc8f557f8 100644
--- a/contrib/libs/llvm14/tools/llvm-strings/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-strings/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-symbolizer/ya.make b/contrib/libs/llvm14/tools/llvm-symbolizer/ya.make
index ca777b10f3..3a7acbf26b 100644
--- a/contrib/libs/llvm14/tools/llvm-symbolizer/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-symbolizer/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-undname/ya.make b/contrib/libs/llvm14/tools/llvm-undname/ya.make
index 439ce5e551..593682721b 100644
--- a/contrib/libs/llvm14/tools/llvm-undname/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-undname/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/llvm-xray/ya.make b/contrib/libs/llvm14/tools/llvm-xray/ya.make
index 06e0b0bb2d..d905463b20 100644
--- a/contrib/libs/llvm14/tools/llvm-xray/ya.make
+++ b/contrib/libs/llvm14/tools/llvm-xray/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/lto/ya.make b/contrib/libs/llvm14/tools/lto/ya.make
index e1bc27df39..e149d3b2ce 100644
--- a/contrib/libs/llvm14/tools/lto/ya.make
+++ b/contrib/libs/llvm14/tools/lto/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/obj2yaml/ya.make b/contrib/libs/llvm14/tools/obj2yaml/ya.make
index b3f0522cce..9b4752d911 100644
--- a/contrib/libs/llvm14/tools/obj2yaml/ya.make
+++ b/contrib/libs/llvm14/tools/obj2yaml/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/opt/ya.make b/contrib/libs/llvm14/tools/opt/ya.make
index 4faf896fd9..b4b0d02440 100644
--- a/contrib/libs/llvm14/tools/opt/ya.make
+++ b/contrib/libs/llvm14/tools/opt/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/ya.make b/contrib/libs/llvm14/tools/polly/lib/External/isl/ya.make
index bc6e9dd500..29d7e550fd 100644
--- a/contrib/libs/llvm14/tools/polly/lib/External/isl/ya.make
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ya.make b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ya.make
index 795b5afca3..9afe1882d9 100644
--- a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ya.make
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(MIT)
diff --git a/contrib/libs/llvm14/tools/polly/lib/ya.make b/contrib/libs/llvm14/tools/polly/lib/ya.make
index 28503484fa..959f2a0c9b 100644
--- a/contrib/libs/llvm14/tools/polly/lib/ya.make
+++ b/contrib/libs/llvm14/tools/polly/lib/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(
diff --git a/contrib/libs/llvm14/tools/remarks-shlib/ya.make b/contrib/libs/llvm14/tools/remarks-shlib/ya.make
index 9c08142a76..966960eced 100644
--- a/contrib/libs/llvm14/tools/remarks-shlib/ya.make
+++ b/contrib/libs/llvm14/tools/remarks-shlib/ya.make
@@ -2,6 +2,8 @@
LIBRARY()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/sancov/ya.make b/contrib/libs/llvm14/tools/sancov/ya.make
index 8f2b4ecca3..cc1aa1b265 100644
--- a/contrib/libs/llvm14/tools/sancov/ya.make
+++ b/contrib/libs/llvm14/tools/sancov/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/sanstats/ya.make b/contrib/libs/llvm14/tools/sanstats/ya.make
index b15632ef0c..f1eccea96c 100644
--- a/contrib/libs/llvm14/tools/sanstats/ya.make
+++ b/contrib/libs/llvm14/tools/sanstats/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/split-file/ya.make b/contrib/libs/llvm14/tools/split-file/ya.make
index b3ed0665e8..cc28284df4 100644
--- a/contrib/libs/llvm14/tools/split-file/ya.make
+++ b/contrib/libs/llvm14/tools/split-file/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/verify-uselistorder/ya.make b/contrib/libs/llvm14/tools/verify-uselistorder/ya.make
index 0cfb5fb2ad..93f4fb4b2b 100644
--- a/contrib/libs/llvm14/tools/verify-uselistorder/ya.make
+++ b/contrib/libs/llvm14/tools/verify-uselistorder/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/libs/llvm14/tools/yaml2obj/ya.make b/contrib/libs/llvm14/tools/yaml2obj/ya.make
index 9834e7075e..feb01e29ee 100644
--- a/contrib/libs/llvm14/tools/yaml2obj/ya.make
+++ b/contrib/libs/llvm14/tools/yaml2obj/ya.make
@@ -2,6 +2,8 @@
PROGRAM()
+SUBSCRIBER(g:cpp-contrib)
+
VERSION(14.0.6)
LICENSE(Apache-2.0 WITH LLVM-exception)
diff --git a/contrib/restricted/googletest/googletest/include/gtest/gtest-printers.h b/contrib/restricted/googletest/googletest/include/gtest/gtest-printers.h
index 6fc97d9143..e3df46b74f 100644
--- a/contrib/restricted/googletest/googletest/include/gtest/gtest-printers.h
+++ b/contrib/restricted/googletest/googletest/include/gtest/gtest-printers.h
@@ -829,6 +829,11 @@ void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) {
*os << ')';
}
+template <typename C, typename D>
+void PrintTo(const ::std::chrono::time_point<C, D>& value, ::std::ostream* os) {
+ *os << "TimeStamp(" << static_cast<int64_t>(value.time_since_epoch().count()) << ")";
+}
+
// Implements printing a non-reference type T by letting the compiler
// pick the right overload of PrintTo() for T.
template <typename T>
diff --git a/library/cpp/http/simple/http_client.h b/library/cpp/http/simple/http_client.h
index 224be58a24..86a8cb4e99 100644
--- a/library/cpp/http/simple/http_client.h
+++ b/library/cpp/http/simple/http_client.h
@@ -267,11 +267,13 @@ TKeepAliveHttpClient::THttpCode TKeepAliveHttpClient::DoRequestReliable(const T&
const bool haveNewConnection = CreateNewConnectionIfNeeded();
const bool couldRetry = !haveNewConnection && i == 0; // Actually old connection could be already closed by server,
// so we should try one more time in this case.
- try {
- cancellation.Future().Subscribe([&](auto&) {
- Connection->Shutdown();
- });
+ TManualEvent cancellationEndEvent;
+ cancellation.Future().Subscribe([&](auto&) {
+ Connection->Shutdown();
+ cancellationEndEvent.Signal();
+ });
+ try {
Connection->Write(raw);
THttpCode code = ReadAndTransferHttp(*Connection->GetHttpInput(), output, outHeaders);
@@ -280,20 +282,29 @@ TKeepAliveHttpClient::THttpCode TKeepAliveHttpClient::DoRequestReliable(const T&
}
return code;
} catch (const TSystemError& e) {
+ if (cancellation.IsCancellationRequested()) {
+ cancellationEndEvent.WaitI();
+ cancellation.ThrowIfCancellationRequested();
+ }
Connection.Reset();
- cancellation.ThrowIfCancellationRequested();
if (!couldRetry || e.Status() != EPIPE) {
throw;
}
} catch (const THttpReadException&) { // Actually old connection is already closed by server
+ if (cancellation.IsCancellationRequested()) {
+ cancellationEndEvent.WaitI();
+ cancellation.ThrowIfCancellationRequested();
+ }
Connection.Reset();
- cancellation.ThrowIfCancellationRequested();
if (!couldRetry) {
throw;
}
} catch (const std::exception&) {
+ if (cancellation.IsCancellationRequested()) {
+ cancellationEndEvent.WaitI();
+ cancellation.ThrowIfCancellationRequested();
+ }
Connection.Reset();
- cancellation.ThrowIfCancellationRequested();
throw;
}
}
diff --git a/library/cpp/neh/http_common.cpp b/library/cpp/neh/http_common.cpp
index 7ae466c31a..23ed6a2889 100644
--- a/library/cpp/neh/http_common.cpp
+++ b/library/cpp/neh/http_common.cpp
@@ -231,5 +231,32 @@ namespace NNeh {
return NeedGetRequestFor(scheme) || NeedPostRequestFor(scheme);
}
}
+
+ HttpCodes GetHttpCode(const IRequest::TResponseError& error) {
+ switch (error) {
+ case IRequest::TResponseError::BadRequest:
+ return HttpCodes::HTTP_BAD_REQUEST;
+ case IRequest::TResponseError::Forbidden:
+ return HttpCodes::HTTP_FORBIDDEN;
+ case IRequest::TResponseError::NotExistService:
+ return HttpCodes::HTTP_NOT_FOUND;
+ case IRequest::TResponseError::TooManyRequests:
+ return HttpCodes::HTTP_TOO_MANY_REQUESTS;
+ case IRequest::TResponseError::InternalError:
+ return HttpCodes::HTTP_INTERNAL_SERVER_ERROR;
+ case IRequest::TResponseError::NotImplemented:
+ return HttpCodes::HTTP_NOT_IMPLEMENTED;
+ case IRequest::TResponseError::BadGateway:
+ return HttpCodes::HTTP_BAD_GATEWAY;
+ case IRequest::TResponseError::ServiceUnavailable:
+ return HttpCodes::HTTP_SERVICE_UNAVAILABLE;
+ case IRequest::TResponseError::BandwidthLimitExceeded:
+ return HttpCodes::HTTP_BANDWIDTH_LIMIT_EXCEEDED;
+ case IRequest::TResponseError::MaxResponseError:
+ ythrow yexception() << TStringBuf("unknow type of error");
+
+ Y_UNREACHABLE();
+ }
+ }
}
diff --git a/library/cpp/neh/http_common.h b/library/cpp/neh/http_common.h
index 69659ba907..acefe80c37 100644
--- a/library/cpp/neh/http_common.h
+++ b/library/cpp/neh/http_common.h
@@ -7,6 +7,7 @@
#include <util/stream/mem.h>
#include <util/stream/output.h>
#include <library/cpp/deprecated/atomic/atomic.h>
+#include <library/cpp/http/misc/httpcodes.h>
#include "location.h"
#include "neh.h"
@@ -298,4 +299,6 @@ namespace NNeh {
bool IsHttpScheme(TStringBuf scheme);
}
+
+ HttpCodes GetHttpCode(const IRequest::TResponseError&);
}
diff --git a/library/cpp/neh/https.cpp b/library/cpp/neh/https.cpp
index f1a2ec9e7e..ece7d3cf2d 100644
--- a/library/cpp/neh/https.cpp
+++ b/library/cpp/neh/https.cpp
@@ -1394,37 +1394,7 @@ namespace NNeh {
return;
}
- switch (*error) {
- case IRequest::TResponseError::BadRequest:
- os << HttpCodeStrEx(HttpCodes::HTTP_BAD_REQUEST);
- break;
- case IRequest::TResponseError::Forbidden:
- os << HttpCodeStrEx(HttpCodes::HTTP_FORBIDDEN);
- break;
- case IRequest::TResponseError::NotExistService:
- os << HttpCodeStrEx(HttpCodes::HTTP_NOT_FOUND);
- break;
- case IRequest::TResponseError::TooManyRequests:
- os << HttpCodeStrEx(HttpCodes::HTTP_TOO_MANY_REQUESTS);
- break;
- case IRequest::TResponseError::InternalError:
- os << HttpCodeStrEx(HttpCodes::HTTP_INTERNAL_SERVER_ERROR);
- break;
- case IRequest::TResponseError::NotImplemented:
- os << HttpCodeStrEx(HttpCodes::HTTP_NOT_IMPLEMENTED);
- break;
- case IRequest::TResponseError::BadGateway:
- os << HttpCodeStrEx(HttpCodes::HTTP_BAD_GATEWAY);
- break;
- case IRequest::TResponseError::ServiceUnavailable:
- os << HttpCodeStrEx(HttpCodes::HTTP_SERVICE_UNAVAILABLE);
- break;
- case IRequest::TResponseError::BandwidthLimitExceeded:
- os << HttpCodeStrEx(HttpCodes::HTTP_BANDWIDTH_LIMIT_EXCEEDED);
- break;
- case IRequest::TResponseError::MaxResponseError:
- ythrow yexception() << TStringBuf("unknow type of error");
- }
+ os << HttpCodeStrEx(GetHttpCode(*error));
}
public:
diff --git a/library/cpp/tdigest/tdigest.cpp b/library/cpp/tdigest/tdigest.cpp
index 145cef78e1..3d3772a9de 100644
--- a/library/cpp/tdigest/tdigest.cpp
+++ b/library/cpp/tdigest/tdigest.cpp
@@ -3,45 +3,52 @@
#include <library/cpp/tdigest/tdigest.pb.h>
#include <cmath>
+#include <util/generic/yexception.h>
// TODO: rewrite to https://github.com/tdunning/t-digest/blob/master/src/main/java/com/tdunning/math/stats/MergingDigest.java
-TDigest::TDigest(double delta, double k)
+TDigest::TDigest(double delta, double k, bool supportsNaN)
: N(0)
, Delta(delta)
, K(k)
+ , SupportsNaN(supportsNaN)
{
}
-TDigest::TDigest(double delta, double k, double firstValue)
- : TDigest(delta, k)
+TDigest::TDigest(double delta, double k, double firstValue, bool supportsNaN)
+ : TDigest(delta, k, supportsNaN)
{
AddValue(firstValue);
}
-TDigest::TDigest(TStringBuf serializedDigest)
+TDigest::TDigest(TStringBuf serializedDigest, bool supportsNaN)
: N(0)
+ , SupportsNaN(supportsNaN)
{
NTDigest::TDigest digest;
Y_ABORT_UNLESS(digest.ParseFromArray(serializedDigest.data(), serializedDigest.size()));
Delta = digest.delta();
K = digest.k();
+ HasNaN = SupportsNaN && digest.nans();
for (int i = 0; i < digest.centroids_size(); ++i) {
const NTDigest::TDigest::TCentroid& centroid = digest.centroids(i);
Update(centroid.mean(), centroid.weight());
}
}
-TDigest::TDigest(const TDigest* digest1, const TDigest* digest2)
+TDigest::TDigest(const TDigest* digest1, const TDigest* digest2, bool supportsNaN)
: N(0)
, Delta(std::min(digest1->Delta, digest2->Delta))
, K(std::max(digest1->K, digest2->K))
+ , SupportsNaN(supportsNaN)
+ , HasNaN(supportsNaN && (digest1->HasNaN || digest2->HasNaN))
{
Add(*digest1);
Add(*digest2);
}
void TDigest::Add(const TDigest& otherDigest) {
+ Y_ENSURE(SupportsNaN == otherDigest.SupportsNaN);
for (auto& it : otherDigest.Centroids)
Update(it.Mean, it.Count);
for (auto& it : otherDigest.Unmerged)
@@ -49,7 +56,8 @@ void TDigest::Add(const TDigest& otherDigest) {
}
TDigest TDigest::operator+(const TDigest& other) {
- TDigest T(Delta, K);
+ Y_ENSURE(SupportsNaN == other.SupportsNaN);
+ TDigest T(Delta, K, SupportsNaN);
T.Add(*this);
T.Add(other);
return T;
@@ -92,6 +100,12 @@ void TDigest::MergeCentroid(TVector<TCentroid>& merged, double& sum, const TCent
}
void TDigest::Update(double x, double w) {
+ if (SupportsNaN) {
+ if (std::isnan(x)) {
+ HasNaN = true;
+ return;
+ }
+ }
AddCentroid(TCentroid(x, w));
if (Unmerged.size() >= K / Delta) {
Compress();
@@ -136,8 +150,17 @@ void TDigest::AddValue(double value) {
double TDigest::GetPercentile(double percentile) {
Compress();
- if (Centroids.empty())
+ if (Centroids.empty()) {
+ if (HasNaN) {
+ return std::numeric_limits<double>::quiet_NaN();
+ }
return 0.0;
+ }
+
+ if (HasNaN && percentile >= 1.0) {
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+
// This algorithm uses C=1/2 with 0.5 optimized away
// See https://en.wikipedia.org/wiki/Percentile#First_Variant.2C
double x = percentile * N;
@@ -159,6 +182,9 @@ double TDigest::GetPercentile(double percentile) {
double TDigest::GetRank(double value) {
Compress();
+ if (SupportsNaN && std::isnan(value)) {
+ return 1.0;
+ }
if (Centroids.empty()) {
return 0.0;
}
@@ -189,6 +215,10 @@ TString TDigest::Serialize() {
NTDigest::TDigest digest;
digest.set_delta(Delta);
digest.set_k(K);
+ if (HasNaN) {
+ digest.set_nans(HasNaN);
+ }
+
for (const auto& it : Centroids) {
NTDigest::TDigest::TCentroid* centroid = digest.add_centroids();
centroid->set_mean(it.Mean);
diff --git a/library/cpp/tdigest/tdigest.h b/library/cpp/tdigest/tdigest.h
index 715620258c..22c87e63c3 100644
--- a/library/cpp/tdigest/tdigest.h
+++ b/library/cpp/tdigest/tdigest.h
@@ -36,6 +36,8 @@ class TDigest {
double N;
double Delta;
double K;
+ bool SupportsNaN = false;
+ bool HasNaN = false;
void Add(const TDigest& otherDigest);
void AddCentroid(const TCentroid& centroid);
@@ -47,10 +49,10 @@ protected:
void Update(double x, double w = 1.0);
public:
- TDigest(double delta = 0.01, double k = 25);
- TDigest(double delta, double k, double firstValue);
- TDigest(TStringBuf serializedDigest);
- TDigest(const TDigest* digest1, const TDigest* digest2); // merge
+ TDigest(double delta = 0.01, double k = 25, bool supportsNaN = false);
+ TDigest(double delta, double k, double firstValue, bool supportsNaN = false);
+ TDigest(TStringBuf serializedDigest, bool supportsNaN = false);
+ TDigest(const TDigest* digest1, const TDigest* digest2, bool supportsNaN = false); // merge
TString Serialize();
diff --git a/library/cpp/tdigest/tdigest.proto b/library/cpp/tdigest/tdigest.proto
index 4a2db3e638..abd8b821cc 100644
--- a/library/cpp/tdigest/tdigest.proto
+++ b/library/cpp/tdigest/tdigest.proto
@@ -8,4 +8,5 @@ message TDigest {
optional double Weight = 2;
}
repeated TCentroid Centroids = 3;
+ optional bool Nans = 4;
}
diff --git a/library/cpp/tld/tlds-alpha-by-domain.txt b/library/cpp/tld/tlds-alpha-by-domain.txt
index fc48570eaf..b534e60ce4 100644
--- a/library/cpp/tld/tlds-alpha-by-domain.txt
+++ b/library/cpp/tld/tlds-alpha-by-domain.txt
@@ -1,4 +1,4 @@
-# Version 2025022200, Last Updated Sat Feb 22 07:07:01 2025 UTC
+# Version 2025030100, Last Updated Sat Mar 1 07:07:02 2025 UTC
AAA
AARP
ABB
@@ -627,7 +627,6 @@ KAUFEN
KDDI
KE
KERRYHOTELS
-KERRYLOGISTICS
KERRYPROPERTIES
KFH
KG
@@ -691,7 +690,6 @@ LIMITED
LIMO
LINCOLN
LINK
-LIPSY
LIVE
LIVING
LK
diff --git a/library/cpp/yt/error/error.cpp b/library/cpp/yt/error/error.cpp
index 636d7d81fc..f097697cd6 100644
--- a/library/cpp/yt/error/error.cpp
+++ b/library/cpp/yt/error/error.cpp
@@ -29,6 +29,8 @@ void FormatValue(TStringBuilderBase* builder, TErrorCode code, TStringBuf spec)
constexpr TStringBuf ErrorMessageTruncatedSuffix = "...<message truncated>";
+TError::TEnricher TError::Enricher_;
+
////////////////////////////////////////////////////////////////////////////////
class TError::TImpl
@@ -275,15 +277,20 @@ TError::TErrorOr(const std::exception& ex)
*this = TError(NYT::EErrorCode::Generic, TRuntimeFormat{ex.what()});
}
YT_VERIFY(!IsOK());
+ Enrich();
}
TError::TErrorOr(std::string message, TDisableFormat)
: Impl_(std::make_unique<TImpl>(std::move(message)))
-{ }
+{
+ Enrich();
+}
TError::TErrorOr(TErrorCode code, std::string message, TDisableFormat)
: Impl_(std::make_unique<TImpl>(code, std::move(message)))
-{ }
+{
+ Enrich();
+}
TError& TError::operator = (const TError& other)
{
@@ -632,6 +639,20 @@ std::optional<TError> TError::FindMatching(const THashSet<TErrorCode>& codes) co
});
}
+void TError::RegisterEnricher(TEnricher enricher)
+{
+ // NB: This daisy-chaining strategy is optimal when there's O(1) callbacks. Convert to a vector
+ // if the number grows.
+ if (Enricher_) {
+ Enricher_ = [first = std::move(Enricher_), second = std::move(enricher)] (TError& error) {
+ first(error);
+ second(error);
+ };
+ } else {
+ Enricher_ = std::move(enricher);
+ }
+}
+
TError::TErrorOr(std::unique_ptr<TImpl> impl)
: Impl_(std::move(impl))
{ }
@@ -643,6 +664,13 @@ void TError::MakeMutable()
}
}
+void TError::Enrich()
+{
+ if (Enricher_) {
+ Enricher_(*this);
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
TError& TError::operator <<= (const TErrorAttribute& attribute) &
diff --git a/library/cpp/yt/error/error.h b/library/cpp/yt/error/error.h
index 8b459d0beb..92f19bc398 100644
--- a/library/cpp/yt/error/error.h
+++ b/library/cpp/yt/error/error.h
@@ -219,6 +219,13 @@ public:
template <CErrorNestable TValue>
TError operator << (const std::optional<TValue>& rhs) const &;
+ // The |enricher| is called during TError construction and before TErrorOr<> construction. Meant
+ // to enrich the error, e.g. by setting generic attributes. The |RegisterEnricher| method is not
+ // threadsafe and is meant to be called from single-threaded bootstrapping code. Multiple
+ // enrichers are supported and will be called in order of registration.
+ using TEnricher = std::function<void(TError&)>;
+ static void RegisterEnricher(TEnricher enricher);
+
private:
class TImpl;
std::unique_ptr<TImpl> Impl_;
@@ -226,8 +233,11 @@ private:
explicit TErrorOr(std::unique_ptr<TImpl> impl);
void MakeMutable();
+ void Enrich();
friend class TErrorAttributes;
+
+ static TEnricher Enricher_;
};
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/python/runtime_py3/main/main.c b/library/python/runtime_py3/main/main.c
index 0c4aa23013..21742d263c 100644
--- a/library/python/runtime_py3/main/main.c
+++ b/library/python/runtime_py3/main/main.c
@@ -74,11 +74,20 @@ static int RunModule(const char* modname)
return 0;
}
-static int pymain(int argc, char** argv) {
+#ifdef MS_WINDOWS
+static int pymain(int argc, wchar_t** argv)
+#else
+static int pymain(int argc, char** argv)
+#endif
+{
PyStatus status;
if (IsYaIdeVenv()) {
+#ifdef MS_WINDOWS
+ return Py_Main(argc, argv);
+#else
return Py_BytesMain(argc, argv);
+#endif
}
status = _PyRuntime_Initialize();
@@ -115,12 +124,20 @@ static int pymain(int argc, char** argv) {
}
if (argc > 0 && argv) {
+#ifdef MS_WINDOWS
+ status = PyConfig_SetString(&config, &config.program_name, argv[0]);
+#else
status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]);
+#endif
if (PyStatus_Exception(status)) {
goto error;
}
+#ifdef MS_WINDOWS
+ status = PyConfig_SetArgv(&config, argc, argv);
+#else
status = PyConfig_SetBytesArgv(&config, argc, argv);
+#endif
if (PyStatus_Exception(status)) {
goto error;
}
@@ -155,7 +172,11 @@ static int pymain(int argc, char** argv) {
}
if (entry_point_copy && !strcmp(entry_point_copy, main_entry_point)) {
+#ifdef MS_WINDOWS
+ sts = Py_Main(argc, argv);
+#else
sts = Py_BytesMain(argc, argv);
+#endif
free(entry_point_copy);
return sts;
}
@@ -217,8 +238,16 @@ error:
return sts;
}
+#ifdef MS_WINDOWS
+int (*mainptr)(int argc, wchar_t** argv) = pymain;
+
+int wmain(int argc, wchar_t** argv) {
+ return mainptr(argc, argv);
+}
+#else
int (*mainptr)(int argc, char** argv) = pymain;
int main(int argc, char** argv) {
return mainptr(argc, argv);
}
+#endif
diff --git a/util/system/backtrace.cpp b/util/system/backtrace.cpp
index 7e3ca7ffcd..97a798e9bb 100644
--- a/util/system/backtrace.cpp
+++ b/util/system/backtrace.cpp
@@ -32,7 +32,7 @@ extern "C" __stdcall unsigned short CaptureStackBackTrace(unsigned long FramesTo
#define HAVE_BACKTRACE
#endif
- #if !defined(HAVE_BACKTRACE) && defined(__GNUC__)
+ #if !defined(HAVE_BACKTRACE) && defined(__GNUC__) && !defined(__EMSCRIPTEN__)
#define USE_GCC_BACKTRACE
#define HAVE_BACKTRACE
#endif
diff --git a/util/system/thread.cpp b/util/system/thread.cpp
index cdde02fa3e..7c880a2909 100644
--- a/util/system/thread.cpp
+++ b/util/system/thread.cpp
@@ -535,11 +535,11 @@ TCurrentThreadLimits::TCurrentThreadLimits() noexcept
: StackBegin(nullptr)
, StackLength(0)
{
-#if defined(_linux_) || defined(_cygwin_) || defined(_freebsd_)
+#if defined(_linux_) || defined(_cygwin_) || defined(_freebsd_) || defined(__EMSCRIPTEN__)
pthread_attr_t attr;
pthread_attr_init(&attr);
- #if defined(_linux_) || defined(_cygwin_)
+ #if defined(_linux_) || defined(_cygwin_) || defined(__EMSCRIPTEN__)
Y_ABORT_UNLESS(pthread_getattr_np(pthread_self(), &attr) == 0, "pthread_getattr failed");
#else
Y_ABORT_UNLESS(pthread_attr_get_np(pthread_self(), &attr) == 0, "pthread_attr_get_np failed");
diff --git a/util/ya.make b/util/ya.make
index 943d8b16cd..dea1f1f721 100644
--- a/util/ya.make
+++ b/util/ya.make
@@ -322,6 +322,7 @@ JOIN_SRCS(
system/sys_alloc.cpp
system/sysstat.cpp
system/tempfile.cpp
+ system/thread.cpp
system/tls.cpp
system/type_name.cpp
system/unaligned_mem.cpp
@@ -335,7 +336,6 @@ JOIN_SRCS(
all_system_4.cpp
system/mem_info.cpp
system/sem.cpp
- system/thread.cpp
system/types.cpp
)
ENDIF()
diff --git a/ydb/ci/rightlib.txt b/ydb/ci/rightlib.txt
index 98e9fec1c8..b7b70c3a1b 100644
--- a/ydb/ci/rightlib.txt
+++ b/ydb/ci/rightlib.txt
@@ -1 +1 @@
-56146bfad396d685118eaaec9cfff0c0675eae75
+6678165e016ba474f1b8dd6d49af92b0d46350b9
diff --git a/ydb/library/yaml_config/ut_transform/canondata/test_transform.TestYamlConfigTransformations.test_basic_args1-dump_ds_init_/nvme.yaml.result.json b/ydb/library/yaml_config/ut_transform/canondata/test_transform.TestYamlConfigTransformations.test_basic_args1-dump_ds_init_/nvme.yaml.result.json
index df02ee3be2..5b916ee9d9 100644
--- a/ydb/library/yaml_config/ut_transform/canondata/test_transform.TestYamlConfigTransformations.test_basic_args1-dump_ds_init_/nvme.yaml.result.json
+++ b/ydb/library/yaml_config/ut_transform/canondata/test_transform.TestYamlConfigTransformations.test_basic_args1-dump_ds_init_/nvme.yaml.result.json
@@ -1 +1 @@
-{"error": true, "stderr": "uncaught exception:\n address -> REDACTED\n what() -> \"ydb/library/yaml_config/deprecated/yaml_config_parser.cpp:855: Specify host_configs to use blobstorage init command\"\n type -> TWithBackTrace<yexception>\n"} \ No newline at end of file
+{"error": true, "stderr": "Terminating due to uncaught exception 0x59e3f140410 what() -> \"ydb/library/yaml_config/deprecated/yaml_config_parser.cpp:855: Specify host_configs to use blobstorage init command\"\n of type TWithBackTrace<yexception>\n"} \ No newline at end of file
diff --git a/yql/essentials/core/facade/ya.make b/yql/essentials/core/facade/ya.make
index 756ed88de4..6164039fa8 100644
--- a/yql/essentials/core/facade/ya.make
+++ b/yql/essentials/core/facade/ya.make
@@ -26,12 +26,6 @@ PEERDIR(
yql/essentials/sql/v1/lexer/antlr4_ansi
yql/essentials/sql/v1/proto_parser/antlr4
yql/essentials/sql/v1/proto_parser/antlr4_ansi
- #FIXME {
- yql/essentials/sql/v1/lexer/antlr3
- yql/essentials/sql/v1/lexer/antlr3_ansi
- yql/essentials/sql/v1/proto_parser/antlr3
- yql/essentials/sql/v1/proto_parser/antlr3_ansi
- #}
yql/essentials/parser/pg_wrapper/interface
yql/essentials/utils/log
yql/essentials/core
diff --git a/yql/essentials/core/facade/yql_facade.cpp b/yql/essentials/core/facade/yql_facade.cpp
index 97a6f54d0b..f8d9c28ba1 100644
--- a/yql/essentials/core/facade/yql_facade.cpp
+++ b/yql/essentials/core/facade/yql_facade.cpp
@@ -12,12 +12,6 @@
#include <yql/essentials/core/services/yql_eval_params.h>
#include <yql/essentials/sql/sql.h>
#include <yql/essentials/sql/v1/sql.h>
-//FIXME {
-#include <yql/essentials/sql/v1/lexer/antlr3/lexer.h>
-#include <yql/essentials/sql/v1/lexer/antlr3_ansi/lexer.h>
-#include <yql/essentials/sql/v1/proto_parser/antlr3/proto_parser.h>
-#include <yql/essentials/sql/v1/proto_parser/antlr3_ansi/proto_parser.h>
-//}
#include <yql/essentials/sql/v1/lexer/antlr4/lexer.h>
#include <yql/essentials/sql/v1/lexer/antlr4_ansi/lexer.h>
#include <yql/essentials/sql/v1/proto_parser/antlr4/proto_parser.h>
@@ -742,13 +736,9 @@ bool TProgram::ParseSql(const NSQLTranslation::TTranslationSettings& settings)
currentSettings->EmitReadsForExists = true;
NSQLTranslationV1::TLexers lexers;
- lexers.Antlr3 = NSQLTranslationV1::MakeAntlr3LexerFactory();
- lexers.Antlr3Ansi = NSQLTranslationV1::MakeAntlr3AnsiLexerFactory();
lexers.Antlr4 = NSQLTranslationV1::MakeAntlr4LexerFactory();
lexers.Antlr4Ansi = NSQLTranslationV1::MakeAntlr4AnsiLexerFactory();
NSQLTranslationV1::TParsers parsers;
- parsers.Antlr3 = NSQLTranslationV1::MakeAntlr3ParserFactory();
- parsers.Antlr3Ansi = NSQLTranslationV1::MakeAntlr3AnsiParserFactory();
parsers.Antlr4 = NSQLTranslationV1::MakeAntlr4ParserFactory();
parsers.Antlr4Ansi = NSQLTranslationV1::MakeAntlr4AnsiParserFactory();
diff --git a/yql/essentials/minikql/computation/llvm16/ya.make b/yql/essentials/minikql/computation/llvm16/ya.make
index 98e800970f..2058603ae1 100644
--- a/yql/essentials/minikql/computation/llvm16/ya.make
+++ b/yql/essentials/minikql/computation/llvm16/ya.make
@@ -6,13 +6,24 @@ PEERDIR(
contrib/libs/llvm16/lib/ExecutionEngine/MCJIT
contrib/libs/llvm16/lib/Linker
contrib/libs/llvm16/lib/Passes
- contrib/libs/llvm16/lib/Target/X86
- contrib/libs/llvm16/lib/Target/X86/AsmParser
- contrib/libs/llvm16/lib/Target/X86/Disassembler
contrib/libs/llvm16/lib/Transforms/IPO
contrib/libs/llvm16/lib/Transforms/ObjCARC
)
+IF (ARCH_X86_64)
+ PEERDIR(
+ contrib/libs/llvm16/lib/Target/X86
+ contrib/libs/llvm16/lib/Target/X86/AsmParser
+ contrib/libs/llvm16/lib/Target/X86/Disassembler
+ )
+ELSEIF (ARCH_AARCH64)
+ PEERDIR(
+ contrib/libs/llvm16/lib/Target/AArch64
+ contrib/libs/llvm16/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm16/lib/Target/AArch64/Disassembler
+ )
+ENDIF()
+
INCLUDE(../ya.make.inc)
END()
diff --git a/yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/epilogue.cmake b/yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/epilogue.cmake
new file mode 100644
index 0000000000..e039c0caf6
--- /dev/null
+++ b/yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/epilogue.cmake
@@ -0,0 +1,8 @@
+set(GRAMMAR_STRING_CORE_SINGLE "~([']) | (QUOTE_SINGLE QUOTE_SINGLE)")
+set(GRAMMAR_STRING_CORE_DOUBLE "~([\"]) | (QUOTE_DOUBLE QUOTE_DOUBLE)")
+set(GRAMMAR_MULTILINE_COMMENT_CORE "MULTILINE_COMMENT | .")
+
+configure_file(
+ ${CMAKE_SOURCE_DIR}/yql/essentials/sql/v1/SQLv1Antlr4.g.in
+ ${CMAKE_BINARY_DIR}/yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/SQLv1Antlr4.g
+)
diff --git a/yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/ya.make b/yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/ya.make
new file mode 100644
index 0000000000..d0b36ae35a
--- /dev/null
+++ b/yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/ya.make
@@ -0,0 +1,43 @@
+LIBRARY()
+
+SET(SQL_GRAMMAR ${ARCADIA_BUILD_ROOT}/${MODDIR}/SQLv1Antlr4.g4)
+
+IF(EXPORT_CMAKE)
+ MANUAL_GENERATION(${SQL_GRAMMAR})
+ELSE()
+ SET(GRAMMAR_STRING_CORE_SINGLE "\"~([']) | (QUOTE_SINGLE QUOTE_SINGLE)\"")
+ SET(GRAMMAR_STRING_CORE_DOUBLE "\"~([#DOUBLE_QUOTE#]) | (QUOTE_DOUBLE QUOTE_DOUBLE)\"")
+ SET(GRAMMAR_MULTILINE_COMMENT_CORE "\"MULTILINE_COMMENT | .\"")
+
+ CONFIGURE_FILE(${ARCADIA_ROOT}/yql/essentials/sql/v1/SQLv1Antlr4.g.in ${SQL_GRAMMAR})
+ENDIF()
+
+COPY_FILE(
+ ${ARCADIA_ROOT}/yql/essentials/parser/antlr_ast/org/antlr/v4/tool/templates/codegen/Cpp/Cpp.stg
+ ${ARCADIA_BUILD_ROOT}/${MODDIR}/org/antlr/v4/tool/templates/codegen/Cpp/Cpp.stg
+)
+
+COPY_FILE(
+ ${ARCADIA_ROOT}/yql/essentials/parser/antlr_ast/org/antlr/v4/tool/templates/codegen/Cpp/Files.stg
+ ${ARCADIA_BUILD_ROOT}/${MODDIR}/org/antlr/v4/tool/templates/codegen/Cpp/Files.stg
+)
+
+RUN_ANTLR4(
+ ${SQL_GRAMMAR}
+ -no-listener
+ -package NALAAnsiAntlr4
+ -lib .
+ IN
+ ${SQL_GRAMMAR}
+ ${ARCADIA_BUILD_ROOT}/${MODDIR}/org/antlr/v4/tool/templates/codegen/Cpp/Cpp.stg
+ ${ARCADIA_BUILD_ROOT}/${MODDIR}/org/antlr/v4/tool/templates/codegen/Cpp/Files.stg
+ OUT SQLv1Antlr4Parser.cpp SQLv1Antlr4Lexer.cpp SQLv1Antlr4Parser.h SQLv1Antlr4Lexer.h
+ OUTPUT_INCLUDES contrib/libs/antlr4_cpp_runtime/src/antlr4-runtime.h
+ CWD ${ARCADIA_BUILD_ROOT}/${MODDIR}
+)
+
+PEERDIR(
+ contrib/libs/antlr4_cpp_runtime
+)
+
+END()
diff --git a/yql/essentials/parser/antlr_ast/gen/v1_antlr4/epilogue.cmake b/yql/essentials/parser/antlr_ast/gen/v1_antlr4/epilogue.cmake
new file mode 100644
index 0000000000..1fb34e7415
--- /dev/null
+++ b/yql/essentials/parser/antlr_ast/gen/v1_antlr4/epilogue.cmake
@@ -0,0 +1,8 @@
+set(GRAMMAR_STRING_CORE_SINGLE "~(['\\\\]) | (BACKSLASH .)")
+set(GRAMMAR_STRING_CORE_DOUBLE "~([\"\\\\]) | (BACKSLASH .)")
+set(GRAMMAR_MULTILINE_COMMENT_CORE ".")
+
+configure_file(
+ ${CMAKE_SOURCE_DIR}/yql/essentials/sql/v1/SQLv1Antlr4.g.in
+ ${CMAKE_BINARY_DIR}/yql/essentials/parser/antlr_ast/gen/v1_antlr4/SQLv1Antlr4.g
+)
diff --git a/yql/essentials/parser/antlr_ast/gen/v1_antlr4/ya.make b/yql/essentials/parser/antlr_ast/gen/v1_antlr4/ya.make
new file mode 100644
index 0000000000..2ef05bd592
--- /dev/null
+++ b/yql/essentials/parser/antlr_ast/gen/v1_antlr4/ya.make
@@ -0,0 +1,43 @@
+LIBRARY()
+
+SET(SQL_GRAMMAR ${ARCADIA_BUILD_ROOT}/${MODDIR}/SQLv1Antlr4.g)
+
+IF(EXPORT_CMAKE)
+ MANUAL_GENERATION(${SQL_GRAMMAR})
+ELSE()
+ SET(GRAMMAR_STRING_CORE_SINGLE "\"~(['#BACKSLASH#]) | (BACKSLASH .)\"")
+ SET(GRAMMAR_STRING_CORE_DOUBLE "\"~([#DOUBLE_QUOTE##BACKSLASH#]) | (BACKSLASH .)\"")
+ SET(GRAMMAR_MULTILINE_COMMENT_CORE "\".\"")
+
+ CONFIGURE_FILE(${ARCADIA_ROOT}/yql/essentials/sql/v1/SQLv1Antlr4.g.in ${SQL_GRAMMAR})
+ENDIF()
+
+COPY_FILE(
+ ${ARCADIA_ROOT}/yql/essentials/parser/antlr_ast/org/antlr/v4/tool/templates/codegen/Cpp/Cpp.stg
+ ${ARCADIA_BUILD_ROOT}/${MODDIR}/org/antlr/v4/tool/templates/codegen/Cpp/Cpp.stg
+)
+
+COPY_FILE(
+ ${ARCADIA_ROOT}/yql/essentials/parser/antlr_ast/org/antlr/v4/tool/templates/codegen/Cpp/Files.stg
+ ${ARCADIA_BUILD_ROOT}/${MODDIR}/org/antlr/v4/tool/templates/codegen/Cpp/Files.stg
+)
+
+RUN_ANTLR4(
+ ${SQL_GRAMMAR}
+ -no-listener
+ -package NALADefaultAntlr4
+ -lib .
+ IN
+ ${SQL_GRAMMAR}
+ ${ARCADIA_BUILD_ROOT}/${MODDIR}/org/antlr/v4/tool/templates/codegen/Cpp/Cpp.stg
+ ${ARCADIA_BUILD_ROOT}/${MODDIR}/org/antlr/v4/tool/templates/codegen/Cpp/Files.stg
+ OUT SQLv1Antlr4Parser.cpp SQLv1Antlr4Lexer.cpp SQLv1Antlr4Parser.h SQLv1Antlr4Lexer.h
+ OUTPUT_INCLUDES contrib/libs/antlr4_cpp_runtime/src/antlr4-runtime.h
+ CWD ${ARCADIA_BUILD_ROOT}/${MODDIR}
+)
+
+PEERDIR(
+ contrib/libs/antlr4_cpp_runtime
+)
+
+END()
diff --git a/yql/essentials/parser/antlr_ast/gen/ya.make b/yql/essentials/parser/antlr_ast/gen/ya.make
new file mode 100644
index 0000000000..2f94911b40
--- /dev/null
+++ b/yql/essentials/parser/antlr_ast/gen/ya.make
@@ -0,0 +1,4 @@
+RECURSE(
+ v1_antlr4
+ v1_ansi_antlr4
+)
diff --git a/yql/essentials/parser/antlr_ast/org/antlr/v4/tool/templates/codegen/Cpp/Cpp.stg b/yql/essentials/parser/antlr_ast/org/antlr/v4/tool/templates/codegen/Cpp/Cpp.stg
new file mode 100644
index 0000000000..c6592680f7
--- /dev/null
+++ b/yql/essentials/parser/antlr_ast/org/antlr/v4/tool/templates/codegen/Cpp/Cpp.stg
@@ -0,0 +1,1176 @@
+/*
+ * [The "BSD license"]
+ * Copyright (c) 2015 Dan McLaughlin, Mike Lischke
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import "Files.stg" // All file specific stuff.
+
+cppTypeInitMap ::= [
+ "int":"0",
+ "long":"0",
+ "float":"0.0f",
+ "double":"0.0",
+ "bool":"false",
+ "short":"0",
+ "char":"0",
+ default: "nullptr" // anything other than a primitive type is an object
+]
+
+LexerHeader(lexer, atn, actionFuncs, sempredFuncs, superClass = {antlr4::Lexer}) ::= <<
+<namedActions.context>
+
+class <file.exportMacro> <lexer.name> : public <superClass> {
+public:
+<if (lexer.tokens)>
+ enum {
+ <lexer.tokens: {k | TOKEN_<k> = <lexer.tokens.(k)>}; separator=", ", wrap, anchor>
+ };
+<endif>
+
+<if (lexer.escapedChannels)>
+ enum {
+ <lexer.escapedChannels: {k | <k> = <lexer.escapedChannels.(k)>}; separator=", ", wrap, anchor>
+ };
+<endif>
+
+<if (rest(lexer.escapedModeNames))>
+ enum {
+ <rest(lexer.escapedModeNames): {m | TOKEN_<m> = <i>}; separator=", ", wrap, anchor>
+ };
+<endif>
+
+ explicit <lexer.name>(antlr4::CharStream *input);
+
+ ~<lexer.name>() override;
+
+ <namedActions.members>
+
+ std::string getGrammarFileName() const override;
+
+ const std::vector\<std::string>& getRuleNames() const override;
+
+ const std::vector\<std::string>& getChannelNames() const override;
+
+ const std::vector\<std::string>& getModeNames() const override;
+
+ const antlr4::dfa::Vocabulary& getVocabulary() const override;
+
+ antlr4::atn::SerializedATNView getSerializedATN() const override;
+
+ const antlr4::atn::ATN& getATN() const override;
+
+ <if (actionFuncs)>
+ void action(antlr4::RuleContext *context, size_t ruleIndex, size_t actionIndex) override;
+ <endif>
+
+ <if (sempredFuncs)>
+ bool sempred(antlr4::RuleContext *_localctx, size_t ruleIndex, size_t predicateIndex) override;
+ <endif>
+
+ // By default the static state used to implement the lexer is lazily initialized during the first
+ // call to the constructor. You can call this function if you wish to initialize the static state
+ // ahead of time.
+ static void initialize();
+
+private:
+ <namedActions.declarations>
+
+ // Individual action functions triggered by action() above.
+ <actionFuncs.values; separator="\n">
+
+ // Individual semantic predicate functions triggered by sempred() above.
+ <sempredFuncs.values; separator="\n">
+
+ <atn>
+};
+>>
+
+Lexer(lexer, atn, actionFuncs, sempredFuncs, superClass = {Lexer}) ::= <<
+
+using namespace antlr4;
+
+namespace {
+
+struct <lexer.name; format = "cap">StaticData final {
+ <lexer.name; format = "cap">StaticData(std::vector\<std::string> ruleNames,
+ std::vector\<std::string> channelNames,
+ std::vector\<std::string> modeNames,
+ std::vector\<std::string> literalNames,
+ std::vector\<std::string> symbolicNames)
+ : ruleNames(std::move(ruleNames)), channelNames(std::move(channelNames)),
+ modeNames(std::move(modeNames)), literalNames(std::move(literalNames)),
+ symbolicNames(std::move(symbolicNames)),
+ vocabulary(this->literalNames, this->symbolicNames) {}
+
+ <lexer.name; format = "cap">StaticData(const <lexer.name; format = "cap">StaticData&) = delete;
+ <lexer.name; format = "cap">StaticData(<lexer.name; format = "cap">StaticData&&) = delete;
+ <lexer.name; format = "cap">StaticData& operator=(const <lexer.name; format = "cap">StaticData&) = delete;
+ <lexer.name; format = "cap">StaticData& operator=(<lexer.name; format = "cap">StaticData&&) = delete;
+
+ std::vector\<antlr4::dfa::DFA> decisionToDFA;
+ antlr4::atn::PredictionContextCache sharedContextCache;
+ const std::vector\<std::string> ruleNames;
+ const std::vector\<std::string> channelNames;
+ const std::vector\<std::string> modeNames;
+ const std::vector\<std::string> literalNames;
+ const std::vector\<std::string> symbolicNames;
+ const antlr4::dfa::Vocabulary vocabulary;
+ antlr4::atn::SerializedATNView serializedATN;
+ std::unique_ptr\<antlr4::atn::ATN> atn;
+};
+
+::antlr4::internal::OnceFlag <lexer.grammarName; format = "lower">LexerOnceFlag;
+#if ANTLR4_USE_THREAD_LOCAL_CACHE
+static thread_local
+#endif
+std::unique_ptr\<<lexer.name; format = "cap">StaticData> <lexer.grammarName; format = "lower">LexerStaticData = nullptr;
+
+void <lexer.grammarName; format = "lower">LexerInitialize() {
+#if ANTLR4_USE_THREAD_LOCAL_CACHE
+ if (<lexer.grammarName; format = "lower">LexerStaticData != nullptr) {
+ return;
+ }
+#else
+ assert(<lexer.grammarName; format = "lower">LexerStaticData == nullptr);
+#endif
+ auto staticData = std::make_unique\<<lexer.name; format = "cap">StaticData>(
+ std::vector\<std::string>{
+ <lexer.ruleNames: {r | "<r>"}; separator = ", ", wrap, anchor>
+ },
+ std::vector\<std::string>{
+ "DEFAULT_TOKEN_CHANNEL", "HIDDEN"<if (lexer.channels)>, <lexer.channels: {c | "<c>"}; separator = ", ", wrap, anchor><endif>
+ },
+ std::vector\<std::string>{
+ <lexer.modes: {m | "<m>"}; separator = ", ", wrap, anchor>
+ },
+ std::vector\<std::string>{
+ <lexer.literalNames: {t | <t>}; null = "\"\"", separator = ", ", wrap, anchor>
+ },
+ std::vector\<std::string>{
+ <lexer.symbolicNames: {t | <t>}; null = "\"\"", separator = ", ", wrap, anchor>
+ }
+ );
+ <atn>
+ <lexer.grammarName; format = "lower">LexerStaticData = std::move(staticData);
+}
+
+}
+
+<lexer.name>::<lexer.name>(CharStream *input) : <superClass>(input) {
+ <lexer.name>::initialize();
+ _interpreter = new atn::LexerATNSimulator(this, *<lexer.grammarName; format = "lower">LexerStaticData->atn, <lexer.grammarName; format = "lower">LexerStaticData->decisionToDFA, <lexer.grammarName; format = "lower">LexerStaticData->sharedContextCache);
+}
+
+<lexer.name>::~<lexer.name>() {
+ delete _interpreter;
+}
+
+std::string <lexer.name>::getGrammarFileName() const {
+ return "<lexer.grammarFileName>";
+}
+
+const std::vector\<std::string>& <lexer.name>::getRuleNames() const {
+ return <lexer.grammarName; format = "lower">LexerStaticData->ruleNames;
+}
+
+const std::vector\<std::string>& <lexer.name>::getChannelNames() const {
+ return <lexer.grammarName; format = "lower">LexerStaticData->channelNames;
+}
+
+const std::vector\<std::string>& <lexer.name>::getModeNames() const {
+ return <lexer.grammarName; format = "lower">LexerStaticData->modeNames;
+}
+
+const dfa::Vocabulary& <lexer.name>::getVocabulary() const {
+ return <lexer.grammarName; format = "lower">LexerStaticData->vocabulary;
+}
+
+antlr4::atn::SerializedATNView <lexer.name>::getSerializedATN() const {
+ return <lexer.grammarName; format = "lower">LexerStaticData->serializedATN;
+}
+
+const atn::ATN& <lexer.name>::getATN() const {
+ return *<lexer.grammarName; format = "lower">LexerStaticData->atn;
+}
+
+<namedActions.definitions>
+
+<if (actionFuncs)>
+void <lexer.name>::action(RuleContext *context, size_t ruleIndex, size_t actionIndex) {
+ switch (ruleIndex) {
+ <lexer.actionFuncs.values: {f | case <f.ruleIndex>: <f.name>Action(antlrcpp::downCast\<<f.ctxType> *>(context), actionIndex); break;}; separator="\n">
+
+ default:
+ break;
+ }
+}
+<endif>
+
+<if (sempredFuncs)>
+bool <lexer.name>::sempred(RuleContext *context, size_t ruleIndex, size_t predicateIndex) {
+ switch (ruleIndex) {
+ <lexer.sempredFuncs.values: {f | case <f.ruleIndex>: return <f.name>Sempred(antlrcpp::downCast\<<f.ctxType> *>(context), predicateIndex);}; separator="\n">
+
+ default:
+ break;
+ }
+ return true;
+}
+<endif>
+
+<actionFuncs.values; separator="\n">
+
+<sempredFuncs.values; separator="\n">
+
+void <lexer.name>::initialize() {
+#if ANTLR4_USE_THREAD_LOCAL_CACHE
+ <lexer.grammarName; format = "lower">LexerInitialize();
+#else
+ ::antlr4::internal::call_once(<lexer.grammarName; format = "lower">LexerOnceFlag, <lexer.grammarName; format = "lower">LexerInitialize);
+#endif
+}
+>>
+
+RuleActionFunctionHeader(r, actions) ::= <<
+void <r.name>Action(<r.ctxType> *context, size_t actionIndex);
+>>
+
+RuleActionFunction(r, actions) ::= <<
+void <r.factory.grammar.name>::<r.name>Action(<r.ctxType> *context, size_t actionIndex) {
+ switch (actionIndex) {
+ <actions: {index | case <index>: <actions.(index)> break;}; separator="\n">
+
+ default:
+ break;
+ }
+}
+
+>>
+
+RuleSempredFunctionHeader(r, actions) ::= <<
+bool <r.name>Sempred(<r.ctxType> *_localctx, size_t predicateIndex);
+>>
+
+RuleSempredFunction(r, actions) ::= <<
+<! Called for both lexer and parser. But only one of them is actually available. Testing for the parser directly
+ generates a warning, however. So do the check via the factory instead. !>
+bool <if (r.factory.g.lexer)><lexer.name><else><parser.name><endif>::<r.name>Sempred(<r.ctxType> *_localctx, size_t predicateIndex) {
+ switch (predicateIndex) {
+ <actions: {index | case <index>: return <actions.(index)>}; separator=";\n">;
+
+ default:
+ break;
+ }
+ return true;
+}
+
+>>
+
+//--------------------------------------------------------------------------------------------------
+
+ParserHeader(parser, funcs, atn, sempredFuncs, superClass = {antlr4::Parser}) ::= <<
+<namedActions.context>
+
+class <file.exportMacro> <parser.name> : public <superClass> {
+public:
+<if (parser.tokens)>
+ enum {
+ <parser.tokens: {k | TOKEN_<k> = <parser.tokens.(k)>}; separator=", ", wrap, anchor>
+ };
+<endif>
+
+<if (parser.rules)>
+ enum {
+ <parser.rules: {r | Rule<r.name; format="cap"> = <r.index>}; separator=", ", wrap, anchor>
+ };
+<endif>
+
+ explicit <parser.name>(antlr4::TokenStream *input);
+
+ <parser.name>(antlr4::TokenStream *input, const antlr4::atn::ParserATNSimulatorOptions &options);
+
+ ~<parser.name>() override;
+
+ std::string getGrammarFileName() const override;
+
+ const antlr4::atn::ATN& getATN() const override;
+
+ const std::vector\<std::string>& getRuleNames() const override;
+
+ const antlr4::dfa::Vocabulary& getVocabulary() const override;
+
+ antlr4::atn::SerializedATNView getSerializedATN() const override;
+
+ <namedActions.members>
+
+ <parser.funcs: {f | class <f.name; format = "cap">Context;}; separator = "\n"> <! Forward declare context classes. !>
+
+ <funcs; separator = "\n">
+
+ <if (sempredFuncs)>
+ bool sempred(antlr4::RuleContext *_localctx, size_t ruleIndex, size_t predicateIndex) override;
+
+ <sempredFuncs.values; separator = "\n">
+ <endif>
+
+ // By default the static state used to implement the parser is lazily initialized during the first
+ // call to the constructor. You can call this function if you wish to initialize the static state
+ // ahead of time.
+ static void initialize();
+
+ static const size_t TOKEN_EOF = antlr4::Token::EOF;
+
+private:
+ <namedActions.declarations>
+};
+>>
+
+Parser(parser, funcs, atn, sempredFuncs, superClass = {Parser}) ::= <<
+
+using namespace antlr4;
+
+namespace {
+
+struct <parser.name; format = "cap">StaticData final {
+ <parser.name; format = "cap">StaticData(std::vector\<std::string> ruleNames,
+ std::vector\<std::string> literalNames,
+ std::vector\<std::string> symbolicNames)
+ : ruleNames(std::move(ruleNames)), literalNames(std::move(literalNames)),
+ symbolicNames(std::move(symbolicNames)),
+ vocabulary(this->literalNames, this->symbolicNames) {}
+
+ <parser.name; format = "cap">StaticData(const <parser.name; format = "cap">StaticData&) = delete;
+ <parser.name; format = "cap">StaticData(<parser.name; format = "cap">StaticData&&) = delete;
+ <parser.name; format = "cap">StaticData& operator=(const <parser.name; format = "cap">StaticData&) = delete;
+ <parser.name; format = "cap">StaticData& operator=(<parser.name; format = "cap">StaticData&&) = delete;
+
+ std::vector\<antlr4::dfa::DFA> decisionToDFA;
+ antlr4::atn::PredictionContextCache sharedContextCache;
+ const std::vector\<std::string> ruleNames;
+ const std::vector\<std::string> literalNames;
+ const std::vector\<std::string> symbolicNames;
+ const antlr4::dfa::Vocabulary vocabulary;
+ antlr4::atn::SerializedATNView serializedATN;
+ std::unique_ptr\<antlr4::atn::ATN> atn;
+};
+
+::antlr4::internal::OnceFlag <parser.grammarName; format = "lower">ParserOnceFlag;
+#if ANTLR4_USE_THREAD_LOCAL_CACHE
+static thread_local
+#endif
+std::unique_ptr\<<parser.name; format = "cap">StaticData> <parser.grammarName; format = "lower">ParserStaticData = nullptr;
+
+void <parser.grammarName; format = "lower">ParserInitialize() {
+#if ANTLR4_USE_THREAD_LOCAL_CACHE
+ if (<parser.grammarName; format = "lower">ParserStaticData != nullptr) {
+ return;
+ }
+#else
+ assert(<parser.grammarName; format = "lower">ParserStaticData == nullptr);
+#endif
+ auto staticData = std::make_unique\<<parser.name; format = "cap">StaticData>(
+ std::vector\<std::string>{
+ <parser.ruleNames: {r | "<r>"}; separator = ", ", wrap, anchor>
+ },
+ std::vector\<std::string>{
+ <parser.literalNames: {t | <t>}; null = "\"\"", separator = ", ", wrap, anchor>
+ },
+ std::vector\<std::string>{
+ <parser.symbolicNames: {t | <t>}; null = "\"\"", separator = ", ", wrap, anchor>
+ }
+ );
+ <atn>
+ <parser.grammarName; format = "lower">ParserStaticData = std::move(staticData);
+}
+
+}
+
+<parser.name>::<parser.name>(TokenStream *input) : <parser.name>(input, antlr4::atn::ParserATNSimulatorOptions()) {}
+
+<parser.name>::<parser.name>(TokenStream *input, const antlr4::atn::ParserATNSimulatorOptions &options) : <superClass>(input) {
+ <parser.name>::initialize();
+ _interpreter = new atn::ParserATNSimulator(this, *<parser.grammarName; format = "lower">ParserStaticData->atn, <parser.grammarName; format = "lower">ParserStaticData->decisionToDFA, <parser.grammarName; format = "lower">ParserStaticData->sharedContextCache, options);
+}
+
+<parser.name>::~<parser.name>() {
+ delete _interpreter;
+}
+
+const atn::ATN& <parser.name>::getATN() const {
+ return *<parser.grammarName; format = "lower">ParserStaticData->atn;
+}
+
+std::string <parser.name>::getGrammarFileName() const {
+ return "<parser.grammarFileName>";
+}
+
+const std::vector\<std::string>& <parser.name>::getRuleNames() const {
+ return <parser.grammarName; format = "lower">ParserStaticData->ruleNames;
+}
+
+const dfa::Vocabulary& <parser.name>::getVocabulary() const {
+ return <parser.grammarName; format = "lower">ParserStaticData->vocabulary;
+}
+
+antlr4::atn::SerializedATNView <parser.name>::getSerializedATN() const {
+ return <parser.grammarName; format = "lower">ParserStaticData->serializedATN;
+}
+
+<namedActions.definitions>
+
+<funcs; separator = "\n\n">
+
+<if (sempredFuncs)>
+bool <parser.name>::sempred(RuleContext *context, size_t ruleIndex, size_t predicateIndex) {
+ switch (ruleIndex) {
+ <parser.sempredFuncs.values: {f |
+ case <f.ruleIndex>: return <f.name>Sempred(antlrcpp::downCast\<<f.ctxType> *>(context), predicateIndex);}; separator="\n">
+
+ default:
+ break;
+ }
+ return true;
+}
+
+<sempredFuncs.values; separator="\n"><endif>
+
+void <parser.name>::initialize() {
+#if ANTLR4_USE_THREAD_LOCAL_CACHE
+ <parser.grammarName; format = "lower">ParserInitialize();
+#else
+ ::antlr4::internal::call_once(<parser.grammarName; format = "lower">ParserOnceFlag, <parser.grammarName; format = "lower">ParserInitialize);
+#endif
+}
+>>
+
+SerializedATNHeader(model) ::= <<
+>>
+
+SerializedATN(model) ::= <<
+static const int32_t serializedATNSegment[] = {
+ <model.serialized: {s | <s>}; separator=",", wrap>
+};
+staticData->serializedATN = antlr4::atn::SerializedATNView(serializedATNSegment, sizeof(serializedATNSegment) / sizeof(serializedATNSegment[0]));
+
+antlr4::atn::ATNDeserializer deserializer;
+staticData->atn = deserializer.deserialize(staticData->serializedATN);
+
+const size_t count = staticData->atn->getNumberOfDecisions();
+staticData->decisionToDFA.reserve(count);
+for (size_t i = 0; i \< count; i++) { <! Rework class ATN to allow standard iterations. !>
+ staticData->decisionToDFA.emplace_back(staticData->atn->getDecisionState(i), i);
+}
+>>
+
+RuleFunctionHeader(currentRule, args, code, locals, ruleCtx, altLabelCtxs, namedActions, finallyAction, postamble, exceptions) ::= <<
+<ruleCtx>
+<! TODO: untested !><if (altLabelCtxs)><altLabelCtxs: {l | <altLabelCtxs.(l)>}; separator="\n"><endif>
+<currentRule.ctxType>* <currentRule.escapedName>(<args; separator=",">);
+
+>>
+
+RuleFunction(currentRule, args, code, locals, ruleCtx, altLabelCtxs, namedActions, finallyAction, postamble, exceptions) ::= <<
+<ruleCtx>
+<! TODO: untested !><altLabelCtxs: {l | <altLabelCtxs.(l)>}; separator = "\n">
+<parser.name>::<currentRule.ctxType>* <parser.name>::<currentRule.escapedName>(<args; separator=",">) {
+ <currentRule.ctxType> *_localctx = _tracker.createInstance\<<currentRule.ctxType>\>(_ctx, getState()<currentRule.args:{a | , <a.escapedName>}>);
+ enterRule(_localctx, <currentRule.startState>, <parser.name>::Rule<currentRule.name; format = "cap">);
+ <namedActions.init>
+ <locals; separator = "\n">
+
+#if __cplusplus > 201703L
+ auto onExit = finally([=, this] {
+#else
+ auto onExit = finally([=] {
+#endif
+ <finallyAction>
+ exitRule();
+ });
+ try {
+<! TODO: untested !><if (currentRule.hasLookaheadBlock)>
+ size_t alt;
+ <endif>
+ <code>
+<! TODO: untested !> <postamble; separator = "\n">
+ <namedActions.after>
+ }
+ <if (exceptions)>
+ <exceptions; separator="\n">
+ <else>
+ catch (RecognitionException &e) {
+ _errHandler->reportError(this, e);
+ _localctx->exception = std::current_exception();
+ _errHandler->recover(this, _localctx->exception);
+ }
+ <endif>
+
+ return _localctx;
+}
+>>
+
+LeftRecursiveRuleFunctionHeader(currentRule, args, code, locals, ruleCtx, altLabelCtxs, namedActions, finallyAction, postamble) ::= <<
+<ruleCtx>
+<! TODO: untested !><altLabelCtxs: {l | <altLabelCtxs.(l)>}; separator="\n">
+<currentRule.ctxType>* <currentRule.escapedName>(<currentRule.args; separator = ", ">);
+<currentRule.ctxType>* <currentRule.escapedName>(int precedence<currentRule.args: {a | , <a>}>);
+>>
+
+LeftRecursiveRuleFunction(currentRule, args, code, locals, ruleCtx, altLabelCtxs, namedActions, finallyAction, postamble) ::= <<
+<ruleCtx>
+<altLabelCtxs: {l | <altLabelCtxs.(l)>}; separator="\n">
+
+<parser.name>::<currentRule.ctxType>* <parser.name>::<currentRule.escapedName>(<currentRule.args; separator=", ">) {
+<! TODO: currentRule.args untested !> return <currentRule.escapedName>(0<currentRule.args: {a | , <a.escapedName>}>);
+}
+
+<parser.name>::<currentRule.ctxType>* <parser.name>::<currentRule.escapedName>(int precedence<currentRule.args:{a | , <a>}>) {
+ ParserRuleContext *parentContext = _ctx;
+ size_t parentState = getState();
+ <parser.name>::<currentRule.ctxType> *_localctx = _tracker.createInstance\<<currentRule.ctxType>\>(_ctx, parentState<currentRule.args: {a | , <a.escapedName>}>);
+ <parser.name>::<currentRule.ctxType> *previousContext = _localctx;
+ (void)previousContext; // Silence compiler, in case the context is not used by generated code.
+ size_t startState = <currentRule.startState>;
+ enterRecursionRule(_localctx, <currentRule.startState>, <parser.name>::Rule<currentRule.name; format = "cap">, precedence);
+
+ <namedActions.init>
+<! TODO: untested !> <locals; separator = "\n">
+
+#if __cplusplus > 201703L
+ auto onExit = finally([=, this] {
+#else
+ auto onExit = finally([=] {
+#endif
+ <if (finallyAction)><finallyAction><endif>
+ unrollRecursionContexts(parentContext);
+ });
+ try {
+ <if (currentRule.hasLookaheadBlock)>size_t alt;<endif>
+ <code>
+<! TODO: untested !><postamble; separator = "\n">
+ <namedActions.after>
+ }
+ catch (RecognitionException &e) {
+ _errHandler->reportError(this, e);
+ _localctx->exception = std::current_exception();
+ _errHandler->recover(this, _localctx->exception);
+ }
+ return _localctx;
+}
+>>
+
+StructDeclHeader(struct, ctorAttrs, attrs, getters, dispatchMethods, interfaces, extensionMembers) ::= <<
+class <file.exportMacro> <struct.escapedName> : public <if (contextSuperClass)><contextSuperClass><else>antlr4::ParserRuleContext<endif><if(interfaces)>, <interfaces; separator=", "><endif> {
+public:
+ <attrs: {a | <a>;}; separator = "\n">
+ <if (ctorAttrs)><struct.escapedName>(antlr4::ParserRuleContext *parent, size_t invokingState);<endif>
+ <struct.escapedName>(antlr4::ParserRuleContext *parent, size_t invokingState<ctorAttrs: {a | , <a>}>);
+<if (struct.provideCopyFrom)> <! don't need copy unless we have subclasses !>
+ <struct.escapedName>() = default;
+ void copyFrom(<struct.escapedName> *context);
+ using antlr4::ParserRuleContext::copyFrom;
+<endif>
+
+ virtual size_t getRuleIndex() const override;
+ <getters: {g | <g>}; separator = "\n">
+
+ <dispatchMethods; separator = "\n">
+<! TODO: untested !> <extensionMembers; separator = "\n">
+};
+
+>>
+
+StructDecl(struct, ctorAttrs, attrs, getters, dispatchMethods, interfaces, extensionMembers, signatures) ::= <<
+//----------------- <struct.escapedName> ------------------------------------------------------------------
+
+<if (ctorAttrs)>
+<parser.name>::<struct.escapedName>::<struct.escapedName>(ParserRuleContext *parent, size_t invokingState)
+ : <if (contextSuperClass)><contextSuperClass><else>ParserRuleContext<endif>(parent, invokingState) {
+}
+<endif>
+
+<parser.name>::<struct.escapedName>::<struct.escapedName>(ParserRuleContext *parent, size_t invokingState<ctorAttrs: {a | , <a>}>)
+ : <if (contextSuperClass)><contextSuperClass><else>ParserRuleContext<endif>(parent, invokingState) {
+ <struct.ctorAttrs: {a | this-><a.escapedName> = <a.escapedName>;}; separator="\n">
+}
+
+<getters: {g | <g>}; separator = "\n">
+
+size_t <parser.name>::<struct.escapedName>::getRuleIndex() const {
+ return <parser.name>::Rule<struct.derivedFromName; format = "cap">;
+}
+
+<if (struct.provideCopyFrom)>
+void <parser.name>::<struct.escapedName>::copyFrom(<struct.escapedName> *ctx) {
+ <if (contextSuperClass)><contextSuperClass><else>ParserRuleContext<endif>::copyFrom(ctx);
+ <struct.attrs: {a | this-><a.escapedName> = ctx-><a.escapedName>;}; separator = "\n">
+}
+<endif>
+<dispatchMethods; separator = "\n\n">
+<! TODO: untested !><extensionMembers; separator = "\n\n">
+
+>>
+
+AltLabelStructDeclHeader(struct, attrs, getters, dispatchMethods) ::= <<
+class <file.exportMacro> <struct.escapedName> : public <currentRule.name; format = "cap">Context {
+public:
+ <struct.escapedName>(<currentRule.name; format = "cap">Context *ctx);
+
+ <if (attrs)><attrs: {a | <a>;}; separator = "\n"><endif>
+ <getters: {g | <g>}; separator = "\n">
+ <dispatchMethods; separator = "\n">
+};
+
+>>
+
+AltLabelStructDecl(struct, attrs, getters, dispatchMethods) ::= <<
+//----------------- <struct.escapedName> ------------------------------------------------------------------
+
+<! TODO: untested !><if (attrs)><attrs: {a | <a>}; separator = "\n"><endif>
+<getters: {g | <g>}; separator = "\n">
+<parser.name>::<struct.escapedName>::<struct.escapedName>(<currentRule.name; format = "cap">Context *ctx) { copyFrom(ctx); }
+
+<dispatchMethods; separator="\n">
+>>
+
+
+CodeBlockForOuterMostAltHeader(currentOuterMostAltCodeBlock, locals, preamble, ops) ::= "<! Required to exist, but unused. !>"
+CodeBlockForOuterMostAlt(currentOuterMostAltCodeBlock, locals, preamble, ops) ::= <<
+<if (currentOuterMostAltCodeBlock.altLabel)>
+_localctx = _tracker.createInstance\<<parser.name>::<currentOuterMostAltCodeBlock.altLabel; format = "cap">Context>(_localctx);
+<endif>
+enterOuterAlt(_localctx, <currentOuterMostAltCodeBlock.alt.altNum>);
+<CodeBlockForAlt(currentAltCodeBlock = currentOuterMostAltCodeBlock, ...)>
+>>
+
+CodeBlockForAltHeader(currentAltCodeBlock, locals, preamble, ops) ::= "<! Required to exist, but unused. !>"
+CodeBlockForAlt(currentAltCodeBlock, locals, preamble, ops) ::= <<
+<! TODO: untested !><locals; separator = "\n">
+<! TODO: untested !><preamble; separator = "\n">
+<ops; separator = "\n">
+>>
+
+LL1AltBlockHeader(choice, preamble, alts, error) ::= "<! Required to exist, but unused. !>"
+LL1AltBlock(choice, preamble, alts, error) ::= <<
+setState(<choice.stateNumber>);
+_errHandler->sync(this);
+<! TODO: untested !><if (choice.label)>LL1AltBlock(choice, preamble, alts, error) <labelref(choice.label)> = _input->LT(1);<endif>
+<preamble; separator="\n">
+switch (_input->LA(1)) {
+ <choice.altLook, alts: {look, alt | <cases(tokens = look)> {
+ <alt>
+ break;
+\}
+}; separator = "\n">
+default:
+ <error>
+}
+>>
+
+LL1OptionalBlockHeader(choice, alts, error) ::= "<! Required but unused. !>"
+LL1OptionalBlock(choice, alts, error) ::= <<
+setState(<choice.stateNumber>);
+_errHandler->sync(this);
+switch (_input->LA(1)) {
+ <choice.altLook, alts: {look, alt | <cases(tokens = look)> {
+ <alt>
+ break;
+\}
+}; separator="\n">
+default:
+ break;
+}
+>>
+
+LL1OptionalBlockSingleAltHeader(choice, expr, alts, preamble, error, followExpr) ::= "<! Required but unused. !>"
+LL1OptionalBlockSingleAlt(choice, expr, alts, preamble, error, followExpr) ::= <<
+setState(<choice.stateNumber>);
+_errHandler->sync(this);
+
+<preamble; separator = "\n">
+if (<expr>) {
+ <alts; separator = "\n">
+}
+>>
+
+LL1StarBlockSingleAltHeader(choice, loopExpr, alts, preamble, iteration) ::= "<! Required but unused. !>"
+LL1StarBlockSingleAlt(choice, loopExpr, alts, preamble, iteration) ::= <<
+setState(<choice.stateNumber>);
+_errHandler->sync(this);
+<preamble; separator="\n">
+while (<loopExpr>) {
+ <alts; separator="\n">
+ setState(<choice.loopBackStateNumber>);
+ _errHandler->sync(this);
+ <iteration>
+}
+>>
+
+LL1PlusBlockSingleAltHeader(choice, loopExpr, alts, preamble, iteration) ::= "<! Required but unused. !>"
+LL1PlusBlockSingleAlt(choice, loopExpr, alts, preamble, iteration) ::= <<
+setState(<choice.blockStartStateNumber>); <! alt block decision !>
+_errHandler->sync(this);
+<preamble; separator="\n">
+do {
+ <alts; separator="\n">
+ setState(<choice.stateNumber>); <! loopback/exit decision !>
+ _errHandler->sync(this);
+ <iteration>
+} while (<loopExpr>);
+>>
+
+// LL(*) stuff
+
+AltBlockHeader(choice, preamble, alts, error) ::= "<! Unused but must be present. !>"
+AltBlock(choice, preamble, alts, error) ::= <<
+setState(<choice.stateNumber>);
+_errHandler->sync(this);
+<! TODO: untested !><if (choice.label)><labelref(choice.label)> = _input->LT(1);<endif>
+<! TODO: untested !><preamble; separator = "\n">
+switch (getInterpreter\<atn::ParserATNSimulator>()->adaptivePredict(_input, <choice.decision>, _ctx)) {
+<alts: {alt | case <i>: {
+ <alt>
+ break;
+\}
+}; separator="\n">
+default:
+ break;
+}
+>>
+
+OptionalBlockHeader(choice, alts, error) ::= "<! Unused but must be present. !>"
+OptionalBlock(choice, alts, error) ::= <<
+setState(<choice.stateNumber>);
+_errHandler->sync(this);
+
+switch (getInterpreter\<atn::ParserATNSimulator>()->adaptivePredict(_input, <choice.decision>, _ctx)) {
+<alts: {alt | case <i><if (!choice.ast.greedy)> + 1<endif>: {
+ <alt>
+ break;
+\}
+}; separator = "\n">
+default:
+ break;
+}
+>>
+
+StarBlockHeader(choice, alts, sync, iteration) ::= "<! Unused but must be present. !>"
+StarBlock(choice, alts, sync, iteration) ::= <<
+setState(<choice.stateNumber>);
+_errHandler->sync(this);
+alt = getInterpreter\<atn::ParserATNSimulator>()->adaptivePredict(_input, <choice.decision>, _ctx);
+while (alt != <choice.exitAlt> && alt != atn::ATN::INVALID_ALT_NUMBER) {
+ if (alt == 1<if(!choice.ast.greedy)> + 1<endif>) {
+ <iteration>
+ <alts> <! should only be one !>
+ }
+ setState(<choice.loopBackStateNumber>);
+ _errHandler->sync(this);
+ alt = getInterpreter\<atn::ParserATNSimulator>()->adaptivePredict(_input, <choice.decision>, _ctx);
+}
+>>
+
+PlusBlockHeader(choice, alts, error) ::= "<! Required to exist, but unused. !>"
+PlusBlock(choice, alts, error) ::= <<
+setState(<choice.blockStartStateNumber>); <! alt block decision !>
+_errHandler->sync(this);
+alt = 1<if(!choice.ast.greedy)> + 1<endif>;
+do {
+ switch (alt) {
+ <alts: {alt | case <i><if (!choice.ast.greedy)> + 1<endif>: {
+ <alt>
+ break;
+ \}
+}; separator="\n">
+ default:
+ <error>
+ }
+ setState(<choice.loopBackStateNumber>); <! loopback/exit decision !>
+ _errHandler->sync(this);
+ alt = getInterpreter\<atn::ParserATNSimulator>()->adaptivePredict(_input, <choice.decision>, _ctx);
+} while (alt != <choice.exitAlt> && alt != atn::ATN::INVALID_ALT_NUMBER);
+>>
+
+Sync(s) ::= "Sync(s) sync(<s.expecting.name>);"
+
+ThrowNoViableAltHeader(t) ::= "<! Unused but must be present. !>"
+ThrowNoViableAlt(t) ::= "throw NoViableAltException(this);"
+
+TestSetInlineHeader(s) ::= "<! Required but unused. !>"
+TestSetInline(s) ::= <<
+<s.bitsets: {bits | <if (rest(rest(bits.tokens)))><bitsetBitfieldComparison(s, bits)><else><bitsetInlineComparison(s, bits)><endif>}; separator=" || ">
+>>
+
+// Java language spec 15.19 - shift operators mask operands rather than overflow to 0... need range test
+testShiftInRange(shiftAmount) ::= <<
+((<shiftAmount> & ~ 0x3fULL) == 0)
+>>
+
+bitsetBitfieldComparison(s, bits) ::= <<
+(<testShiftInRange({<offsetShift(s.varName, bits.shift)>})> &&
+ ((1ULL \<\< <offsetShift(s.varName, bits.shift)>) & <bits.calculated>) != 0)
+>>
+
+isZero ::= [
+ "0": true,
+ default: false
+]
+
+offsetShift(shiftAmount, offset, prefix = false) ::= <%
+<if (!isZero.(offset))>(<if (prefix)><parser.name>::TOKEN_<endif><shiftAmount> - <offset>)<else><if (prefix)><parser.name>::TOKEN_<endif><shiftAmount><endif>
+%>
+
+bitsetInlineComparison(s, bits) ::= <%
+<bits.tokens: {t | <s.varName> == <parser.name>::TOKEN_<t.name>}; separator = "\n\n|| ">
+%>
+
+cases(tokens) ::= <<
+<tokens: {t | case <parser.name>::TOKEN_<t.name>:}; separator="\n">
+>>
+
+InvokeRuleHeader(r, argExprsChunks) ::= "InvokeRuleHeader"
+InvokeRule(r, argExprsChunks) ::= <<
+setState(<r.stateNumber>);
+<if(r.labels)><r.labels: {l | <labelref(l)> = }><endif><r.escapedName>(<if(r.ast.options.p)><r.ast.options.p><if(argExprsChunks)>,<endif><endif><argExprsChunks>);
+>>
+
+MatchTokenHeader(m) ::= "<! Required but unused. !>"
+MatchToken(m) ::= <<
+setState(<m.stateNumber>);
+<if (m.labels)><m.labels: {l | <labelref(l)> = }><endif>match(<parser.name>::TOKEN_<m.escapedName>);
+>>
+
+MatchSetHeader(m, expr, capture) ::= "<! Required but unused. !>"
+MatchSet(m, expr, capture) ::= "<CommonSetStuff(m, expr, capture, false)>"
+
+MatchNotSetHeader(m, expr, capture) ::= "<! Required but unused. !>"
+MatchNotSet(m, expr, capture) ::= "<CommonSetStuff(m, expr, capture, true)>"
+
+CommonSetStuff(m, expr, capture, invert) ::= <<
+setState(<m.stateNumber>);
+<if (m.labels)><m.labels: {l | <labelref(l)> = }>_input->LT(1);<endif>
+<capture>
+if (<if (invert)><m.varName> == 0 || <m.varName> == Token::EOF || <else>!<endif>(<expr>)) {
+ <if (m.labels)><m.labels: {l | <labelref(l)> = }><endif>_errHandler->recoverInline(this);
+}
+else {
+ _errHandler->reportMatch(this);
+ consume();
+}
+>>
+
+WildcardHeader(w) ::= "<! Required but unused. !>"
+Wildcard(w) ::= <<
+setState(<w.stateNumber>);
+<if (w.labels)><w.labels: {l | <labelref(l)> = }><endif>matchWildcard();
+>>
+
+// ACTION STUFF
+
+ActionHeader(a, foo, chunks) ::= "<chunks>"
+Action(a, foo, chunks) ::= "<chunks>"
+
+ArgAction(a, chunks) ::= "ArgAction(a, chunks) <chunks>"
+
+SemPredHeader(p, chunks, failChunks) ::= "<! Required but unused. !>"
+SemPred(p, chunks, failChunks) ::= <<
+setState(<p.stateNumber>);
+
+if (!(<chunks>)) throw FailedPredicateException(this, <p.predicate><if (failChunks)>, <failChunks><elseif (p.msg)>, <p.msg><endif>);
+>>
+
+ExceptionClauseHeader(e, catchArg, catchAction) ::= "<! Required but unused. !>"
+ExceptionClause(e, catchArg, catchAction) ::= <<
+catch (<catchArg>) {
+ <catchAction>
+}
+>>
+
+// Lexer actions are not associated with model objects.
+
+LexerSkipCommand() ::= "skip();"
+LexerMoreCommand() ::= "more();"
+LexerPopModeCommand() ::= "popMode();"
+
+LexerTypeCommand(arg, grammar) ::= "type = <grammar.name>::<arg>;"
+LexerChannelCommand(arg, grammar) ::= "channel = <arg>;"
+LexerModeCommand(arg, grammar) ::= "mode = <grammar.name>Mode::<arg>;"
+LexerPushModeCommand(arg, grammar) ::= "pushMode(<grammar.name>Mode::<arg>);"
+
+ActionTextHeader(t) ::= "<t.text>"
+ActionText(t) ::= "<t.text>"
+
+ActionTemplateHeader(t) ::= "<! Required but unused. !>"
+ActionTemplate(t) ::= "<t.st>"
+
+ArgRefHeader(t) ::= "<! Required but unused. !>"
+ArgRef(a) ::= "_localctx-><a.escapedName>"
+
+LocalRefHeader(t) ::= "<! Required but unused. !>"
+LocalRef(a) ::= "_localctx-><a.escapedName>"
+
+RetValueRefHeader(t) ::= "<! Required but unused. !>"
+RetValueRef(a) ::= "_localctx-><a.escapedName>"
+
+QRetValueRefHeader(t) ::= "<! Required but unused. !>"
+QRetValueRef(a) ::= "<ctx(a)>-><a.dict>-><a.escapedName>"
+/** How to translate $tokenLabel */
+
+TokenRefHeader(t) ::= "<! Required but unused. !>"
+TokenRef(t) ::= "<ctx(t)>-><t.escapedName>"
+
+LabelRefHeader(t) ::= "<! Required but unused. !>"
+LabelRef(t) ::= "<ctx(t)>-><t.escapedName>"
+
+ListLabelRefHeader(t) ::= "<! Required but unused. !>"
+ListLabelRef(t) ::= "<ctx(t)>-><ListLabelName(t.escapedName)>"
+
+SetAttrHeader(t) ::= "<! Required but unused. !>"
+SetAttr(s,rhsChunks) ::= "<ctx(s)>-><s.escapedName> = <rhsChunks>;"
+
+InputSymbolType() ::= "<file.InputSymbolType; null = {Token}> *"
+
+TokenPropertyRef_textHeader(t) ::= "<! Required but unused. !>"
+TokenPropertyRef_text(t) ::= <<(<ctx(t)>-><t.label> != nullptr ? <ctx(t)>-><t.label>->getText() : "")>>
+
+TokenPropertyRef_typeHeader(t) ::= "<! Required but unused. !>"
+TokenPropertyRef_type(t) ::= "(<ctx(t)>-><t.label> != nullptr ? <ctx(t)>-><t.label>->getType() : 0)"
+
+TokenPropertyRef_lineHeader(t) ::= "<! Required but unused. !>"
+TokenPropertyRef_line(t) ::= "(<ctx(t)>-><t.label> != nullptr ? <ctx(t)>-><t.label>->getLine() : 0)"
+
+TokenPropertyRef_posHeader(t) ::= "<! Required but unused. !>"
+TokenPropertyRef_pos(t) ::= "(<ctx(t)>-><t.label> != nullptr ? <ctx(t)>-><t.label>->getCharPositionInLine() : 0)"
+
+TokenPropertyRef_channelHeader(t) ::= "<! Required but unused. !>"
+TokenPropertyRef_channel(t) ::= "(<ctx(t)>-><t.label> != nullptr ? <ctx(t)>-><t.label>->getChannel() : 0)"
+
+TokenPropertyRef_indexHeader(t) ::= "<! Required but unused. !>"
+TokenPropertyRef_index(t) ::= "(<ctx(t)>-><t.label> != nullptr ? <ctx(t)>-><t.label>->getTokenIndex() : 0)"
+
+TokenPropertyRef_intHeader(t) ::= "<! Required but unused. !>"
+TokenPropertyRef_int(t) ::= "(<ctx(t)>-><t.label> != nullptr ? std::stoi(<ctx(t)>-><t.label>->getText()) : 0)"
+
+RulePropertyRef_startHeader(r) ::= "<! Required but unused. !>"
+RulePropertyRef_start(r) ::= "(<ctx(r)>-><r.label> != nullptr ? (<ctx(r)>-><r.label>->start) : nullptr)"
+
+RulePropertyRef_stopHeader(r) ::= "<! Required but unused. !>"
+RulePropertyRef_stop(r) ::= "(<ctx(r)>-><r.label> != nullptr ? (<ctx(r)>-><r.label>->stop) : nullptr)"
+
+RulePropertyRef_textHeader(r) ::= "<! Required but unused. !>"
+RulePropertyRef_text(r) ::= "(<ctx(r)>-><r.label> != nullptr ? _input->getText(<ctx(r)>-><r.label>->start, <ctx(r)>-><r.label>->stop) : nullptr)"
+
+RulePropertyRef_ctxHeader(r) ::= "<! Required but unused. !>"
+RulePropertyRef_ctx(r) ::= "<ctx(r)>-><r.label>"
+
+ThisRulePropertyRef_start(r) ::= "ThisRulePropertyRef_start(r) _localctx->start"
+ThisRulePropertyRef_stop(r) ::= "ThisRulePropertyRef_stop(r) _localctx->stop"
+
+ThisRulePropertyRef_textHeader(r) ::= "<! Required but unused. !>"
+ThisRulePropertyRef_text(r) ::= "_input->getText(_localctx->start, _input->LT(-1))"
+
+ThisRulePropertyRef_ctxHeader(r) ::= "<! Required but unused. !>"
+ThisRulePropertyRef_ctx(r) ::= "_localctx"
+
+ThisRulePropertyRef_parserHeader(r) ::= "<! Required but unused. !>"
+ThisRulePropertyRef_parser(r) ::= "this"
+
+NonLocalAttrRef(s) ::= "NonLocalAttrRef(s) ((<s.ruleName; format=\"cap\">Context)getInvokingContext(<s.ruleIndex>)).<s.escapedName>"
+SetNonLocalAttr(s, rhsChunks) ::=
+ "SetNonLocalAttr(s, rhsChunks) ((<s.ruleName; format=\"cap\">Context)getInvokingContext(<s.ruleIndex>)).<s.escapedName> = <rhsChunks>;"
+
+AddToLabelListHeader(a) ::= "<! Required but unused. !>"
+AddToLabelList(a) ::= <<
+<ctx(a.label)>-><a.listName>.push_back(<labelref(a.label)>);
+>>
+
+TokenLabelType() ::= "<file.TokenLabelType; null = {Token}> *"
+
+TokenDeclHeader(t) ::= "antlr4::<TokenLabelType()><t.escapedName> = nullptr"
+TokenDecl(t) ::= "<! Variable Declaration !>"
+
+TokenTypeDeclHeader(t) ::= "<! Local Variable !>"
+TokenTypeDecl(t) ::= "size_t <t.escapedName> = 0;"
+
+TokenListDeclHeader(t) ::= "std::vector\<antlr4::Token *> <t.escapedName>"
+TokenListDecl(t) ::= "<! Variable Declaration !>"
+
+RuleContextDeclHeader(r) ::= "<parser.name>::<r.ctxName> *<r.escapedName> = nullptr"
+RuleContextDecl(r) ::= "<! Variable Declaration !>"
+
+RuleContextListDeclHeader(rdecl) ::= "std::vector\<<rdecl.ctxName> *> <rdecl.escapedName>"
+RuleContextListDecl(rdecl) ::= "<! Variable Declaration !>"
+
+ContextTokenGetterDeclHeader(t) ::= "antlr4::tree::TerminalNode *TOKEN_<t.escapedName>();"
+ContextTokenGetterDecl(t) ::= <<
+tree::TerminalNode* <parser.name>::<t.ctx.name>::TOKEN_<t.escapedName>() {
+ return getToken(<parser.name>::TOKEN_<t.escapedName>, 0);
+}
+
+>>
+
+ContextTokenListGetterDeclHeader(t) ::= "std::vector\<antlr4::tree::TerminalNode *> TOKEN_<t.escapedName>();"
+ContextTokenListGetterDecl(t) ::= <<
+std::vector\<tree::TerminalNode *> <parser.name>::<t.ctx.name>::TOKEN_<t.escapedName>() {
+ return getTokens(<parser.name>::TOKEN_<t.escapedName>);
+}
+
+>>
+
+ContextTokenListIndexedGetterDeclHeader(t) ::= "antlr4::tree::TerminalNode* TOKEN_<t.escapedName>(size_t i);"
+ContextTokenListIndexedGetterDecl(t) ::= <<
+tree::TerminalNode* <parser.name>::<t.ctx.name>::TOKEN_<t.escapedName>(size_t i) {
+ return getToken(<parser.name>::TOKEN_<t.escapedName>, i);
+}
+
+>>
+
+ContextRuleGetterDeclHeader(r) ::= "<r.ctxName> *<r.escapedName>();"
+ContextRuleGetterDecl(r) ::= <<
+<! Note: ctxName is the name of the context to return, while ctx is the owning context. !>
+<parser.name>::<r.ctxName>* <parser.name>::<r.ctx.name>::<r.escapedName>() {
+ return getRuleContext\<<parser.name>::<r.ctxName>\>(0);
+}
+
+>>
+
+ContextRuleListGetterDeclHeader(r) ::= "std::vector\<<r.ctxName> *> <r.escapedName>();"
+ContextRuleListGetterDecl(r) ::= <<
+std::vector\<<parser.name>::<r.ctxName> *> <parser.name>::<r.ctx.name>::<r.escapedName>() {
+ return getRuleContexts\<<parser.name>::<r.ctxName>\>();
+}
+
+>>
+
+ContextRuleListIndexedGetterDeclHeader(r) ::= "<r.ctxName>* <r.escapedName>(size_t i);"
+ContextRuleListIndexedGetterDecl(r) ::= <<
+<parser.name>::<r.ctxName>* <parser.name>::<r.ctx.name>::<r.escapedName>(size_t i) {
+ return getRuleContext\<<parser.name>::<r.ctxName>\>(i);
+}
+
+>>
+
+LexerRuleContext() ::= "antlr4::RuleContext"
+
+// The rule context name is the rule followed by a suffix; e.g. r becomes rContext.
+RuleContextNameSuffix() ::= "Context"
+
+ImplicitTokenLabel(tokenName) ::= <<
+<tokenName; format = "lower">Token
+>>
+
+ImplicitRuleLabel(ruleName) ::= "<ruleName>Context"
+ImplicitSetLabel(id) ::= "_tset<id>"
+ListLabelName(label) ::= "<label>"
+
+CaptureNextToken(d) ::= "CaptureNextToken(d) <d.varName> = _input->LT(1);"
+
+CaptureNextTokenTypeHeader(d) ::= "<! Required but unused. !>"
+CaptureNextTokenType(d) ::= "<d.varName> = _input->LA(1);"
+
+ListenerDispatchMethodHeader(method) ::= <<
+virtual void <if (method.isEnter)>enter<else>exit<endif>Rule(antlr4::tree::ParseTreeListener *listener) override;
+>>
+ListenerDispatchMethod(method) ::= <<
+void <parser.name>::<struct.escapedName>::<if (method.isEnter)>enter<else>exit<endif>Rule(tree::ParseTreeListener *listener) {
+ auto parserListener = dynamic_cast\<<parser.grammarName>Listener *>(listener);
+ if (parserListener != nullptr)
+ parserListener-><if(method.isEnter)>enter<else>exit<endif><struct.derivedFromName; format="cap">(this);
+}
+>>
+
+VisitorDispatchMethodHeader(method) ::= <<
+
+virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
+>>
+VisitorDispatchMethod(method) ::= <<
+
+std::any <parser.name>::<struct.escapedName>::accept(tree::ParseTreeVisitor *visitor) {
+ if (auto parserVisitor = dynamic_cast\<<parser.grammarName>Visitor*>(visitor))
+ return parserVisitor->visit<struct.derivedFromName; format="cap">(this);
+ else
+ return visitor->visitChildren(this);
+}
+>>
+
+AttributeDeclHeader(d) ::= "<d.type> <d.escapedName><if(d.initValue)> = <d.initValue><endif>"
+AttributeDecl(d) ::= "<d.type> <d.escapedName>"
+
+/** If we don't know location of label def x, use this template */
+labelref(x) ::= "<if (!x.isLocal)>antlrcpp::downCast\<<x.ctx.name> *>(_localctx)-><endif><x.escapedName>"
+
+/** For any action chunk, what is correctly-typed context struct ptr? */
+ctx(actionChunk) ::= "antlrcpp::downCast\<<actionChunk.ctx.name> *>(_localctx)"
+
+// used for left-recursive rules
+recRuleAltPredicate(ruleName,opPrec) ::= "precpred(_ctx, <opPrec>)"
+recRuleSetReturnAction(src,name) ::= "recRuleSetReturnAction(src,name) $<name>=$<src>.<name>;"
+recRuleSetStopToken() ::= "_ctx->stop = _input->LT(-1);"
+
+recRuleAltStartAction(ruleName, ctxName, label, isListLabel) ::= <<
+_localctx = _tracker.createInstance\<<ctxName>Context>(parentContext, parentState);
+<if(label)>
+<if(isListLabel)>
+_localctx-><label>.push_back(previousContext);
+<else>
+_localctx-><label> = previousContext;
+<endif>
+<endif>
+pushNewRecursionContext(_localctx, startState, Rule<ruleName; format = "cap">);
+>>
+
+// Separate context variable to avoid frequent pointer type casts.
+recRuleLabeledAltStartAction(ruleName, currentAltLabel, label, isListLabel) ::= <<
+auto newContext = _tracker.createInstance\<<currentAltLabel; format = "cap">Context>(_tracker.createInstance\<<ruleName; format="cap">Context>(parentContext, parentState));
+_localctx = newContext;
+<if(label)>
+<if(isListLabel)>
+newContext-><label>.push_back(previousContext);
+<else>
+newContext-><label> = previousContext;
+<endif>
+<endif>
+pushNewRecursionContext(newContext, startState, Rule<ruleName; format = "cap">);
+>>
+
+recRuleReplaceContext(ctxName) ::= <<
+_localctx = _tracker.createInstance\<<ctxName>Context>(_localctx);
+_ctx = _localctx;
+previousContext = _localctx;
+>>
+
+recRuleSetPrevCtx() ::= <<
+if (!_parseListeners.empty())
+ triggerExitRuleEvent();
+previousContext = _localctx;
+>>
+
+/** Using a type to init value map, try to init a type; if not in table
+ * must be an object, default value is "null".
+ */
+initValue(typeName) ::= <<
+<cppTypeInitMap.(typeName)>
+>> \ No newline at end of file
diff --git a/yql/essentials/parser/antlr_ast/org/antlr/v4/tool/templates/codegen/Cpp/Files.stg b/yql/essentials/parser/antlr_ast/org/antlr/v4/tool/templates/codegen/Cpp/Files.stg
new file mode 100644
index 0000000000..c2a36eb758
--- /dev/null
+++ b/yql/essentials/parser/antlr_ast/org/antlr/v4/tool/templates/codegen/Cpp/Files.stg
@@ -0,0 +1,344 @@
+/*
+ * [The "BSD license"]
+ * Copyright (c) 2016, Mike Lischke
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+codeFileExtension() ::= ".cpp"
+headerFileExtension() ::= ".h"
+
+fileHeader(grammarFileName, ANTLRVersion, header) ::= <<
+<header>
+
+// Generated from <grammarFileName> by ANTLR <ANTLRVersion>
+>>
+
+LexerFileHeader(file, lexer, namedActions) ::= <<
+<fileHeader(file.grammarFileName, file.ANTLRVersion, namedActions.header)>
+
+#pragma once
+
+<namedActions.preinclude>
+
+#include "antlr4-runtime.h"
+
+<namedActions.postinclude>
+
+<if(file.genPackage)>namespace <file.genPackage> {<endif>
+
+<lexer>
+
+<if (file.genPackage)>
+} // namespace <file.genPackage>
+<endif>
+>>
+
+LexerFile(file, lexer, namedActions) ::= <<
+<fileHeader(file.grammarFileName, file.ANTLRVersion, namedActions.header)>
+
+<namedActions.preinclude>
+
+#include "<file.lexer.name>.h"
+
+<namedActions.postinclude>
+
+using namespace antlr4;
+
+<if (file.genPackage)>using namespace <file.genPackage>;<endif>
+
+<lexer>
+
+>>
+
+ParserFileHeader(file, parser, namedActions, contextSuperClass) ::= <<
+<fileHeader(file.grammarFileName, file.ANTLRVersion, namedActions.header)>
+
+#pragma once
+
+<namedActions.preinclude>
+
+#include "antlr4-runtime.h"
+
+<namedActions.postinclude>
+
+<if (file.genPackage)>namespace <file.genPackage> {<endif>
+
+<parser>
+
+<if (file.genPackage)>
+} // namespace <file.genPackage>
+<endif>
+>>
+
+ParserFile(file, parser, namedActions, contextSuperClass) ::= <<
+<fileHeader(file.grammarFileName, file.ANTLRVersion, namedActions.header)>
+
+<namedActions.preinclude>
+
+<if (file.genListener)>#include "<file.grammarName>Listener.h"<endif>
+<if (file.genVisitor)>#include "<file.grammarName>Visitor.h"<endif>
+
+#include "<file.parser.name>.h"
+
+<namedActions.postinclude>
+
+using namespace antlrcpp;
+<if (file.genPackage)>using namespace <file.genPackage>;<endif>
+<parser>
+
+>>
+
+BaseListenerFileHeader(file, header, namedActions) ::= <<
+<fileHeader(file.grammarFileName, file.ANTLRVersion, header)>
+
+#pragma once
+
+<namedActions.baselistenerpreinclude>
+
+#include "antlr4-runtime.h"
+#include "<file.grammarName>Listener.h"
+
+<namedActions.baselistenerpostinclude>
+
+<if(file.genPackage)>
+namespace <file.genPackage> {
+<endif>
+
+/**
+ * This class provides an empty implementation of <file.grammarName>Listener,
+ * which can be extended to create a listener which only needs to handle a subset
+ * of the available methods.
+ */
+class <file.exportMacro> <file.grammarName>BaseListener : public <file.grammarName>Listener {
+public:
+<namedActions.baselistenerdeclarations>
+
+<file.listenerNames: {lname |
+ virtual void enter<lname; format="cap">(<file.parserName>::<lname; format = "cap">Context * /*ctx*/) override { \}
+ virtual void exit<lname; format="cap">(<file.parserName>::<lname; format = "cap">Context * /*ctx*/) override { \}
+}; separator="\n">
+
+ virtual void enterEveryRule(antlr4::ParserRuleContext * /*ctx*/) override { }
+ virtual void exitEveryRule(antlr4::ParserRuleContext * /*ctx*/) override { }
+ virtual void visitTerminal(antlr4::tree::TerminalNode * /*node*/) override { }
+ virtual void visitErrorNode(antlr4::tree::ErrorNode * /*node*/) override { }
+
+<if (namedActions.baselistenermembers)>
+private:
+<namedActions.baselistenermembers>
+<endif>
+};
+
+<if (file.genPackage)>
+} // namespace <file.genPackage>
+<endif>
+>>
+
+BaseListenerFile(file, header, namedActions) ::= <<
+<fileHeader(file.grammarFileName, file.ANTLRVersion, header)>
+
+<namedActions.baselistenerpreinclude>
+
+#include "<file.grammarName>BaseListener.h"
+
+<namedActions.baselistenerpostinclude>
+
+<if(file.genPackage)>
+using namespace <file.genPackage>;
+<endif>
+
+<namedActions.baselistenerdefinitions>
+>>
+
+ListenerFileHeader(file, header, namedActions) ::= <<
+<fileHeader(file.grammarFileName, file.ANTLRVersion, header)>
+
+#pragma once
+
+<namedActions.listenerpreinclude>
+
+#include "antlr4-runtime.h"
+#include "<file.parserName>.h"
+
+<namedActions.listenerpostinclude>
+
+<if(file.genPackage)>
+namespace <file.genPackage> {
+<endif>
+
+/**
+ * This interface defines an abstract listener for a parse tree produced by <file.parserName>.
+ */
+class <file.exportMacro> <file.grammarName>Listener : public antlr4::tree::ParseTreeListener {
+public:
+<namedActions.listenerdeclarations>
+
+<file.listenerNames: {lname |
+ virtual void enter<lname; format = "cap">(<file.parserName>::<lname; format ="cap">Context *ctx) = 0;
+ virtual void exit<lname; format = "cap">(<file.parserName>::<lname; format="cap">Context *ctx) = 0;
+}; separator = "\n">
+
+<if (namedActions.listenermembers)>
+private:
+<namedActions.listenermembers>
+<endif>
+};
+
+<if(file.genPackage)>
+} // namespace <file.genPackage>
+<endif>
+>>
+
+ListenerFile(file, header, namedActions) ::= <<
+<fileHeader(file.grammarFileName, file.ANTLRVersion, header)>
+
+<namedActions.listenerpreinclude>
+
+#include "<file.grammarName>Listener.h"
+
+<namedActions.listenerpostinclude>
+
+<if(file.genPackage)>
+using namespace <file.genPackage>;
+<endif>
+
+<namedActions.listenerdefinitions>
+>>
+
+BaseVisitorFileHeader(file, header, namedActions) ::= <<
+<fileHeader(file.grammarFileName, file.ANTLRVersion, header)>
+
+#pragma once
+
+<namedActions.basevisitorpreinclude>
+
+#include "antlr4-runtime.h"
+#include "<file.grammarName>Visitor.h"
+
+<namedActions.basevisitorpostinclude>
+
+<if(file.genPackage)>
+namespace <file.genPackage> {
+<endif>
+
+/**
+ * This class provides an empty implementation of <file.grammarName>Visitor, which can be
+ * extended to create a visitor which only needs to handle a subset of the available methods.
+ */
+class <file.exportMacro> <file.grammarName>BaseVisitor : public <file.grammarName>Visitor {
+public:
+<namedActions.basevisitordeclarations>
+
+<file.visitorNames: { lname |
+ virtual std::any visit<lname; format = "cap">(<file.parserName>::<lname; format = "cap">Context *ctx) override {
+ return visitChildren(ctx);
+ \}
+}; separator="\n">
+
+<if (namedActions.basevisitormembers)>
+private:
+<namedActions.basevisitormembers>
+<endif>
+};
+
+<if(file.genPackage)>
+} // namespace <file.genPackage>
+<endif>
+>>
+
+BaseVisitorFile(file, header, namedActions) ::= <<
+<fileHeader(file.grammarFileName, file.ANTLRVersion, header)>
+
+<namedActions.basevisitorpreinclude>
+
+#include "<file.grammarName>BaseVisitor.h"
+
+<namedActions.basevisitorpostinclude>
+
+<if(file.genPackage)>
+using namespace <file.genPackage>;
+<endif>
+
+<namedActions.basevisitordefinitions>
+
+>>
+
+VisitorFileHeader(file, header, namedActions) ::= <<
+<fileHeader(file.grammarFileName, file.ANTLRVersion, header)>
+
+#pragma once
+
+<namedActions.visitorpreinclude>
+
+#include "antlr4-runtime.h"
+#include "<file.parserName>.h"
+
+<namedActions.visitorpostinclude>
+
+<if(file.genPackage)>namespace <file.genPackage> {<endif>
+
+/**
+ * This class defines an abstract visitor for a parse tree
+ * produced by <file.parserName>.
+ */
+class <file.exportMacro> <file.grammarName>Visitor : public antlr4::tree::AbstractParseTreeVisitor {
+public:
+ <namedActions.visitordeclarations>
+
+ /**
+ * Visit parse trees produced by <file.parserName>.
+ */
+ <file.visitorNames: {lname |
+ virtual std::any visit<lname; format = "cap">(<file.parserName>::<lname; format = "cap">Context *context) = 0;
+ }; separator="\n">
+
+<if (namedActions.visitormembers)>
+private:
+<namedActions.visitormembers>
+<endif>
+};
+
+<if(file.genPackage)>
+} // namespace <file.genPackage>
+<endif>
+>>
+
+VisitorFile(file, header, namedActions) ::= <<
+<fileHeader(file.grammarFileName, file.ANTLRVersion, header)>
+
+<namedActions.visitorpreinclude>
+
+#include "<file.grammarName>Visitor.h"
+
+<namedActions.visitorpostinclude>
+
+<if(file.genPackage)>
+using namespace <file.genPackage>;
+<endif>
+
+<namedActions.visitordefinitions>
+
+>> \ No newline at end of file
diff --git a/yql/essentials/parser/antlr_ast/ya.make b/yql/essentials/parser/antlr_ast/ya.make
new file mode 100644
index 0000000000..d3b33a2dfa
--- /dev/null
+++ b/yql/essentials/parser/antlr_ast/ya.make
@@ -0,0 +1,4 @@
+RECURSE(
+ gen
+)
+
diff --git a/yql/essentials/parser/ya.make b/yql/essentials/parser/ya.make
index 7f1b0fe700..c1cacaa6a8 100644
--- a/yql/essentials/parser/ya.make
+++ b/yql/essentials/parser/ya.make
@@ -1,4 +1,5 @@
RECURSE(
+ antlr_ast
lexer_common
pg_catalog
pg_wrapper
diff --git a/yql/essentials/public/fastcheck/translator.cpp b/yql/essentials/public/fastcheck/translator.cpp
index eec337be86..8350dd9b4f 100644
--- a/yql/essentials/public/fastcheck/translator.cpp
+++ b/yql/essentials/public/fastcheck/translator.cpp
@@ -61,6 +61,17 @@ private:
settings.Antlr4Parser = true;
settings.AnsiLexer = request.IsAnsiLexer;
settings.SyntaxVersion = request.SyntaxVersion;
+ settings.Flags.insert({
+ "AnsiOrderByLimitInUnionAll",
+ "DisableCoalesceJoinKeysOnQualifiedAll",
+ "AnsiRankForNullableKeys",
+ "DisableUnorderedSubqueries",
+ "DisableAnsiOptionalAs",
+ "FlexibleTypes",
+ "CompactNamedExprs",
+ "DistinctOverWindow"
+ });
+
switch (request.Mode) {
case EMode::Default:
settings.AlwaysAllowExports = true;
diff --git a/yql/essentials/public/purecalc/common/worker_factory.cpp b/yql/essentials/public/purecalc/common/worker_factory.cpp
index 50b85ed05a..7597c7fdd2 100644
--- a/yql/essentials/public/purecalc/common/worker_factory.cpp
+++ b/yql/essentials/public/purecalc/common/worker_factory.cpp
@@ -7,13 +7,6 @@
#include <yql/essentials/sql/sql.h>
#include <yql/essentials/sql/v1/sql.h>
-//FIXME {
-#include <yql/essentials/sql/v1/lexer/antlr3/lexer.h>
-#include <yql/essentials/sql/v1/lexer/antlr3_ansi/lexer.h>
-#include <yql/essentials/sql/v1/proto_parser/antlr3/proto_parser.h>
-#include <yql/essentials/sql/v1/proto_parser/antlr3_ansi/proto_parser.h>
-//}
-
#include <yql/essentials/sql/v1/lexer/antlr4/lexer.h>
#include <yql/essentials/sql/v1/lexer/antlr4_ansi/lexer.h>
#include <yql/essentials/sql/v1/proto_parser/antlr4/proto_parser.h>
@@ -152,6 +145,7 @@ TExprNode::TPtr TWorkerFactory<TBase>::Compile(
bool useAntlr4,
EProcessorMode processorMode
) {
+ Y_ENSURE(useAntlr4, "Antlr3 support is dropped");
if (mode == ETranslationMode::PG && processorMode != EProcessorMode::PullList) {
ythrow TCompileError("", "") << "only PullList mode is compatible to PostgreSQL syntax";
}
@@ -221,13 +215,9 @@ TExprNode::TPtr TWorkerFactory<TBase>::Compile(
}
NSQLTranslationV1::TLexers lexers;
- lexers.Antlr3 = NSQLTranslationV1::MakeAntlr3LexerFactory();
- lexers.Antlr3Ansi = NSQLTranslationV1::MakeAntlr3AnsiLexerFactory();
lexers.Antlr4 = NSQLTranslationV1::MakeAntlr4LexerFactory();
lexers.Antlr4Ansi = NSQLTranslationV1::MakeAntlr4AnsiLexerFactory();
NSQLTranslationV1::TParsers parsers;
- parsers.Antlr3 = NSQLTranslationV1::MakeAntlr3ParserFactory();
- parsers.Antlr3Ansi = NSQLTranslationV1::MakeAntlr3AnsiParserFactory();
parsers.Antlr4 = NSQLTranslationV1::MakeAntlr4ParserFactory();
parsers.Antlr4Ansi = NSQLTranslationV1::MakeAntlr4AnsiParserFactory();
diff --git a/yql/essentials/public/purecalc/common/ya.make.inc b/yql/essentials/public/purecalc/common/ya.make.inc
index 4059a9e78d..0f3e3d9370 100644
--- a/yql/essentials/public/purecalc/common/ya.make.inc
+++ b/yql/essentials/public/purecalc/common/ya.make.inc
@@ -43,12 +43,6 @@ PEERDIR(
yql/essentials/sql/v1/lexer/antlr4_ansi
yql/essentials/sql/v1/proto_parser/antlr4
yql/essentials/sql/v1/proto_parser/antlr4_ansi
- #{ FIXME
- yql/essentials/sql/v1/lexer/antlr3
- yql/essentials/sql/v1/lexer/antlr3_ansi
- yql/essentials/sql/v1/proto_parser/antlr3
- yql/essentials/sql/v1/proto_parser/antlr3_ansi
- #}
yql/essentials/parser/pg_wrapper/interface
yql/essentials/providers/common/codec
yql/essentials/providers/common/comp_nodes
diff --git a/yql/essentials/sql/v1/SQLv1.g.in b/yql/essentials/sql/v1/SQLv1.g.in
index 1d7dca5185..dbec682803 100644
--- a/yql/essentials/sql/v1/SQLv1.g.in
+++ b/yql/essentials/sql/v1/SQLv1.g.in
@@ -845,8 +845,9 @@ role_name: an_id_or_type | bind_parameter;
user_option: authentication_option | login_option;
authentication_option: password_option | hash_option;
-password_option: ENCRYPTED? PASSWORD expr;
-hash_option: HASH expr;
+password_option: ENCRYPTED? PASSWORD password_value;
+password_value: STRING_VALUE | NULL;
+hash_option: HASH STRING_VALUE;
login_option: LOGIN | NOLOGIN;
grant_permissions_stmt: GRANT permission_name_target ON an_id_schema (COMMA an_id_schema)* TO role_name (COMMA role_name)* COMMA? (WITH GRANT OPTION)?;
diff --git a/yql/essentials/sql/v1/SQLv1Antlr4.g.in b/yql/essentials/sql/v1/SQLv1Antlr4.g.in
index 8aff2bb89a..fb92a68f9a 100644
--- a/yql/essentials/sql/v1/SQLv1Antlr4.g.in
+++ b/yql/essentials/sql/v1/SQLv1Antlr4.g.in
@@ -844,8 +844,9 @@ role_name: an_id_or_type | bind_parameter;
user_option: authentication_option | login_option;
authentication_option: password_option | hash_option;
-password_option: ENCRYPTED? PASSWORD expr;
-hash_option: HASH expr;
+password_option: ENCRYPTED? PASSWORD password_value;
+password_value: STRING_VALUE | NULL;
+hash_option: HASH STRING_VALUE;
login_option: LOGIN | NOLOGIN;
grant_permissions_stmt: GRANT permission_name_target ON an_id_schema (COMMA an_id_schema)* TO role_name (COMMA role_name)* COMMA? (WITH GRANT OPTION)?;
diff --git a/yql/essentials/sql/v1/complete/c3_engine.h b/yql/essentials/sql/v1/complete/c3_engine.h
new file mode 100644
index 0000000000..8b729f880b
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/c3_engine.h
@@ -0,0 +1,116 @@
+#pragma once
+
+#include "sql_antlr4.h"
+#include "string_util.h"
+
+#include <contrib/libs/antlr4_cpp_runtime/src/ANTLRInputStream.h>
+#include <contrib/libs/antlr4_cpp_runtime/src/BufferedTokenStream.h>
+#include <contrib/libs/antlr4_cpp_runtime/src/Vocabulary.h>
+#include <contrib/libs/antlr4-c3/src/CodeCompletionCore.hpp>
+
+#include <util/generic/string.h>
+#include <util/generic/vector.h>
+
+#include <vector>
+#include <unordered_set>
+
+namespace NSQLComplete {
+
+ // std::vector is used to prevent copying from c3 results
+ struct TSuggestedToken {
+ TTokenId Number;
+ std::vector<TRuleId> ParserCallStack;
+ };
+
+ class IC3Engine {
+ public:
+ using TPtr = THolder<IC3Engine>;
+
+ // std::unordered_set is used to prevent copying into c3 core
+ struct TConfig {
+ std::unordered_set<TTokenId> IgnoredTokens;
+ std::unordered_set<TRuleId> PreferredRules;
+ };
+
+ virtual TVector<TSuggestedToken> Complete(TStringBuf queryPrefix) = 0;
+ virtual const antlr4::dfa::Vocabulary& GetVocabulary() const = 0;
+ virtual ~IC3Engine() = default;
+ };
+
+ template <class Lexer, class Parser>
+ struct TAntlrGrammar {
+ using TLexer = Lexer;
+ using TParser = Parser;
+
+ TAntlrGrammar() = delete;
+ };
+
+ template <class G>
+ class TC3Engine: public IC3Engine {
+ public:
+ explicit TC3Engine(TConfig config)
+ : Chars()
+ , Lexer(&Chars)
+ , Tokens(&Lexer)
+ , Parser(&Tokens)
+ , CompletionCore(&Parser)
+ {
+ Lexer.removeErrorListeners();
+ Parser.removeErrorListeners();
+
+ CompletionCore.ignoredTokens = std::move(config.IgnoredTokens);
+ CompletionCore.preferredRules = std::move(config.PreferredRules);
+ }
+
+ TVector<TSuggestedToken> Complete(TStringBuf queryPrefix) override {
+ Assign(queryPrefix);
+ const auto caretTokenIndex = CaretTokenIndex(queryPrefix);
+ auto candidates = CompletionCore.collectCandidates(caretTokenIndex);
+ return Converted(std::move(candidates));
+ }
+
+ const antlr4::dfa::Vocabulary& GetVocabulary() const override {
+ return Lexer.getVocabulary();
+ }
+
+ private:
+ void Assign(TStringBuf queryPrefix) {
+ Chars.load(queryPrefix.Data(), queryPrefix.Size(), /* lenient = */ false);
+ Lexer.reset();
+ Tokens.setTokenSource(&Lexer);
+
+ Tokens.fill();
+ }
+
+ size_t CaretTokenIndex(TStringBuf queryPrefix) {
+ const auto tokensCount = Tokens.size();
+ if (2 <= tokensCount && !LastWord(queryPrefix).Empty()) {
+ return tokensCount - 2;
+ }
+ return tokensCount - 1;
+ }
+
+ static TVector<TSuggestedToken> Converted(c3::CandidatesCollection candidates) {
+ TVector<TSuggestedToken> converted;
+ for (const auto& [token, _] : candidates.tokens) {
+ std::vector<TRuleId> parserCallStack;
+
+ if (
+ auto rules = candidates.rules.find(token);
+ rules != std::end(candidates.rules)) {
+ parserCallStack = std::move(rules->second.ruleList);
+ }
+
+ converted.emplace_back(token, std::move(parserCallStack));
+ }
+ return converted;
+ }
+
+ antlr4::ANTLRInputStream Chars;
+ G::TLexer Lexer;
+ antlr4::BufferedTokenStream Tokens;
+ G::TParser Parser;
+ c3::CodeCompletionCore CompletionCore;
+ };
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/sql_antlr4.cpp b/yql/essentials/sql/v1/complete/sql_antlr4.cpp
new file mode 100644
index 0000000000..33c847f3e2
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/sql_antlr4.cpp
@@ -0,0 +1,116 @@
+#include "sql_antlr4.h"
+
+#include <yql/essentials/sql/v1/format/sql_format.h>
+
+#include <yql/essentials/parser/antlr_ast/gen/v1_antlr4/SQLv1Antlr4Lexer.h>
+#include <yql/essentials/parser/antlr_ast/gen/v1_antlr4/SQLv1Antlr4Parser.h>
+#include <yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/SQLv1Antlr4Lexer.h>
+#include <yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/SQLv1Antlr4Parser.h>
+
+#define RULE_(mode, name) NALA##mode##Antlr4::SQLv1Antlr4Parser::Rule##name
+
+#define RULE(name) RULE_(Default, name)
+
+#define STATIC_ASSERT_RULE_ID_MODE_INDEPENDENT(name) \
+ static_assert(RULE_(Default, name) == RULE_(Ansi, name))
+
+namespace NSQLComplete {
+
+ class TSqlGrammar: public ISqlGrammar {
+ public:
+ TSqlGrammar(bool isAnsiLexer)
+ : Vocabulary(GetVocabulary(isAnsiLexer))
+ , AllTokens(ComputeAllTokens())
+ , KeywordTokens(ComputeKeywordTokens())
+ {
+ }
+
+ const antlr4::dfa::Vocabulary& GetVocabulary() const override {
+ return *Vocabulary;
+ }
+
+ const std::unordered_set<TTokenId>& GetAllTokens() const override {
+ return AllTokens;
+ }
+
+ const std::unordered_set<TTokenId>& GetKeywordTokens() const override {
+ return KeywordTokens;
+ }
+
+ const TVector<TRuleId>& GetKeywordRules() const override {
+ static const TVector<TRuleId> KeywordRules = {
+ RULE(Keyword),
+ RULE(Keyword_expr_uncompat),
+ RULE(Keyword_table_uncompat),
+ RULE(Keyword_select_uncompat),
+ RULE(Keyword_alter_uncompat),
+ RULE(Keyword_in_uncompat),
+ RULE(Keyword_window_uncompat),
+ RULE(Keyword_hint_uncompat),
+ RULE(Keyword_as_compat),
+ RULE(Keyword_compat),
+ };
+
+ STATIC_ASSERT_RULE_ID_MODE_INDEPENDENT(Keyword);
+ STATIC_ASSERT_RULE_ID_MODE_INDEPENDENT(Keyword_expr_uncompat);
+ STATIC_ASSERT_RULE_ID_MODE_INDEPENDENT(Keyword_table_uncompat);
+ STATIC_ASSERT_RULE_ID_MODE_INDEPENDENT(Keyword_select_uncompat);
+ STATIC_ASSERT_RULE_ID_MODE_INDEPENDENT(Keyword_alter_uncompat);
+ STATIC_ASSERT_RULE_ID_MODE_INDEPENDENT(Keyword_in_uncompat);
+ STATIC_ASSERT_RULE_ID_MODE_INDEPENDENT(Keyword_window_uncompat);
+ STATIC_ASSERT_RULE_ID_MODE_INDEPENDENT(Keyword_hint_uncompat);
+ STATIC_ASSERT_RULE_ID_MODE_INDEPENDENT(Keyword_as_compat);
+ STATIC_ASSERT_RULE_ID_MODE_INDEPENDENT(Keyword_compat);
+
+ return KeywordRules;
+ }
+
+ private:
+ static const antlr4::dfa::Vocabulary* GetVocabulary(bool isAnsiLexer) {
+ if (isAnsiLexer) { // Taking a reference is okay as vocabulary storage is static
+ return &NALAAnsiAntlr4::SQLv1Antlr4Parser(nullptr).getVocabulary();
+ }
+ return &NALADefaultAntlr4::SQLv1Antlr4Parser(nullptr).getVocabulary();
+ }
+
+ std::unordered_set<TTokenId> ComputeAllTokens() {
+ const auto& vocabulary = GetVocabulary();
+
+ std::unordered_set<TTokenId> allTokens;
+
+ for (size_t type = 1; type <= vocabulary.getMaxTokenType(); ++type) {
+ allTokens.emplace(type);
+ }
+
+ return allTokens;
+ }
+
+ std::unordered_set<TTokenId> ComputeKeywordTokens() {
+ const auto& vocabulary = GetVocabulary();
+ const auto keywords = NSQLFormat::GetKeywords();
+
+ auto keywordTokens = GetAllTokens();
+ std::erase_if(keywordTokens, [&](TTokenId token) {
+ return !keywords.contains(vocabulary.getSymbolicName(token));
+ });
+ keywordTokens.erase(TOKEN_EOF);
+
+ return keywordTokens;
+ }
+
+ const antlr4::dfa::Vocabulary* Vocabulary;
+ const std::unordered_set<TTokenId> AllTokens;
+ const std::unordered_set<TTokenId> KeywordTokens;
+ };
+
+ const ISqlGrammar& GetSqlGrammar(bool isAnsiLexer) {
+ const static TSqlGrammar DefaultSqlGrammar(/* isAnsiLexer = */ false);
+ const static TSqlGrammar AnsiSqlGrammar(/* isAnsiLexer = */ true);
+
+ if (isAnsiLexer) {
+ return AnsiSqlGrammar;
+ }
+ return DefaultSqlGrammar;
+ }
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/sql_antlr4.h b/yql/essentials/sql/v1/complete/sql_antlr4.h
new file mode 100644
index 0000000000..3d3c4c024a
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/sql_antlr4.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "sql_syntax.h"
+
+#include <contrib/libs/antlr4_cpp_runtime/src/Token.h>
+#include <contrib/libs/antlr4_cpp_runtime/src/Vocabulary.h>
+
+#include <unordered_set>
+
+namespace NSQLComplete {
+
+ using TTokenId = size_t;
+ using TRuleId = size_t;
+
+ constexpr TTokenId TOKEN_EOF = antlr4::Token::EOF;
+
+ class ISqlGrammar {
+ public:
+ virtual const antlr4::dfa::Vocabulary& GetVocabulary() const = 0;
+ virtual const std::unordered_set<TTokenId>& GetAllTokens() const = 0;
+ virtual const std::unordered_set<TTokenId>& GetKeywordTokens() const = 0;
+ virtual const TVector<TRuleId>& GetKeywordRules() const = 0;
+ virtual ~ISqlGrammar() = default;
+ };
+
+ const ISqlGrammar& GetSqlGrammar(bool isAnsiLexer);
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/sql_complete.cpp b/yql/essentials/sql/v1/complete/sql_complete.cpp
new file mode 100644
index 0000000000..2a16a250e5
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/sql_complete.cpp
@@ -0,0 +1,89 @@
+#include "sql_complete.h"
+
+#include "sql_context.h"
+#include "string_util.h"
+
+#include <util/generic/algorithm.h>
+#include <util/charset/utf8.h>
+
+namespace NSQLComplete {
+
+ class TSqlCompletionEngine: public ISqlCompletionEngine {
+ public:
+ TSqlCompletionEngine()
+ : ContextInference(MakeSqlContextInference())
+ {
+ }
+
+ TCompletion Complete(TCompletionInput input) {
+ auto prefix = input.Text.Head(input.CursorPosition);
+ auto completedToken = GetCompletedToken(prefix);
+
+ auto context = ContextInference->Analyze(input);
+
+ TVector<TCandidate> candidates;
+ EnrichWithKeywords(candidates, context.Keywords);
+
+ FilterByContent(candidates, completedToken.Content);
+
+ RankingSort(candidates);
+
+ return {
+ .CompletedToken = std::move(completedToken),
+ .Candidates = std::move(candidates),
+ };
+ }
+
+ private:
+ TCompletedToken GetCompletedToken(TStringBuf prefix) {
+ return {
+ .Content = LastWord(prefix),
+ .SourcePosition = LastWordIndex(prefix),
+ };
+ }
+
+ void EnrichWithKeywords(TVector<TCandidate>& candidates, TVector<TString> keywords) {
+ for (auto keyword : keywords) {
+ candidates.push_back({
+ .Kind = ECandidateKind::Keyword,
+ .Content = std::move(keyword),
+ });
+ }
+ }
+
+ void FilterByContent(TVector<TCandidate>& candidates, TStringBuf prefix) {
+ const auto lowerPrefix = ToLowerUTF8(prefix);
+ auto removed = std::ranges::remove_if(candidates, [&](const auto& candidate) {
+ return !ToLowerUTF8(candidate.Content).StartsWith(lowerPrefix);
+ });
+ candidates.erase(std::begin(removed), std::end(removed));
+ }
+
+ void RankingSort(TVector<TCandidate>& candidates) {
+ Sort(candidates, [](const TCandidate& lhs, const TCandidate& rhs) {
+ return std::tie(lhs.Kind, lhs.Content) < std::tie(rhs.Kind, rhs.Content);
+ });
+ }
+
+ ISqlContextInference::TPtr ContextInference;
+ };
+
+ ISqlCompletionEngine::TPtr MakeSqlCompletionEngine() {
+ return ISqlCompletionEngine::TPtr(new TSqlCompletionEngine());
+ }
+
+} // namespace NSQLComplete
+
+template <>
+void Out<NSQLComplete::ECandidateKind>(IOutputStream& out, NSQLComplete::ECandidateKind kind) {
+ switch (kind) {
+ case NSQLComplete::ECandidateKind::Keyword:
+ out << "Keyword";
+ break;
+ }
+}
+
+template <>
+void Out<NSQLComplete::TCandidate>(IOutputStream& out, const NSQLComplete::TCandidate& candidate) {
+ out << "(" << candidate.Kind << ": " << candidate.Content << ")";
+}
diff --git a/yql/essentials/sql/v1/complete/sql_complete.h b/yql/essentials/sql/v1/complete/sql_complete.h
new file mode 100644
index 0000000000..99e74cce7a
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/sql_complete.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <util/generic/string.h>
+#include <util/generic/vector.h>
+
+namespace NSQLComplete {
+
+ struct TCompletionInput {
+ TStringBuf Text;
+ size_t CursorPosition = Text.length();
+ };
+
+ struct TCompletedToken {
+ TStringBuf Content;
+ size_t SourcePosition;
+ };
+
+ enum class ECandidateKind {
+ Keyword,
+ };
+
+ struct TCandidate {
+ ECandidateKind Kind;
+ TString Content;
+
+ friend bool operator==(const TCandidate& lhs, const TCandidate& rhs) = default;
+ };
+
+ struct TCompletion {
+ TCompletedToken CompletedToken;
+ TVector<TCandidate> Candidates;
+ };
+
+ class ISqlCompletionEngine {
+ public:
+ using TPtr = THolder<ISqlCompletionEngine>;
+
+ virtual TCompletion Complete(TCompletionInput input) = 0;
+ virtual ~ISqlCompletionEngine() = default;
+ };
+
+ ISqlCompletionEngine::TPtr MakeSqlCompletionEngine();
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
new file mode 100644
index 0000000000..e0a012f9f6
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
@@ -0,0 +1,323 @@
+#include "sql_complete.h"
+
+#include <library/cpp/testing/unittest/registar.h>
+
+using namespace NSQLComplete;
+
+Y_UNIT_TEST_SUITE(SqlCompleteTests) {
+ using ECandidateKind::Keyword;
+
+ TVector<TCandidate> Complete(ISqlCompletionEngine::TPtr& engine, TStringBuf prefix) {
+ return engine->Complete({prefix}).Candidates;
+ }
+
+ Y_UNIT_TEST(Beginning) {
+ TVector<TCandidate> expected = {
+ {Keyword, "ALTER"},
+ {Keyword, "ANALYZE"},
+ {Keyword, "BACKUP"},
+ {Keyword, "BATCH"},
+ {Keyword, "COMMIT"},
+ {Keyword, "CREATE"},
+ {Keyword, "DECLARE"},
+ {Keyword, "DEFINE"},
+ {Keyword, "DELETE"},
+ {Keyword, "DISCARD"},
+ {Keyword, "DO"},
+ {Keyword, "DROP"},
+ {Keyword, "EVALUATE"},
+ {Keyword, "EXPLAIN"},
+ {Keyword, "EXPORT"},
+ {Keyword, "FOR"},
+ {Keyword, "FROM"},
+ {Keyword, "GRANT"},
+ {Keyword, "IF"},
+ {Keyword, "IMPORT"},
+ {Keyword, "INSERT"},
+ {Keyword, "PARALLEL"},
+ {Keyword, "PRAGMA"},
+ {Keyword, "PROCESS"},
+ {Keyword, "REDUCE"},
+ {Keyword, "REPLACE"},
+ {Keyword, "RESTORE"},
+ {Keyword, "REVOKE"},
+ {Keyword, "ROLLBACK"},
+ {Keyword, "SELECT"},
+ {Keyword, "SHOW"},
+ {Keyword, "UPDATE"},
+ {Keyword, "UPSERT"},
+ {Keyword, "USE"},
+ {Keyword, "VALUES"},
+ };
+
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {""}), expected);
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {" "}), expected);
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {" "}), expected);
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {";"}), expected);
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"; "}), expected);
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {" ; "}), expected);
+ }
+
+ Y_UNIT_TEST(Alter) {
+ TVector<TCandidate> expected = {
+ {Keyword, "ASYNC"},
+ {Keyword, "BACKUP"},
+ {Keyword, "DATABASE"},
+ {Keyword, "EXTERNAL"},
+ {Keyword, "GROUP"},
+ {Keyword, "OBJECT"},
+ {Keyword, "RESOURCE"},
+ {Keyword, "SEQUENCE"},
+ {Keyword, "TABLE"},
+ {Keyword, "TABLESTORE"},
+ {Keyword, "TOPIC"},
+ {Keyword, "TRANSFER"},
+ {Keyword, "USER"},
+ };
+
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"ALTER "}), expected);
+ }
+
+ Y_UNIT_TEST(Create) {
+ TVector<TCandidate> expected = {
+ {Keyword, "ASYNC"},
+ {Keyword, "BACKUP"},
+ {Keyword, "EXTERNAL"},
+ {Keyword, "GROUP"},
+ {Keyword, "OBJECT"},
+ {Keyword, "OR"},
+ {Keyword, "RESOURCE"},
+ {Keyword, "TABLE"},
+ {Keyword, "TABLESTORE"},
+ {Keyword, "TEMP"},
+ {Keyword, "TEMPORARY"},
+ {Keyword, "TOPIC"},
+ {Keyword, "TRANSFER"},
+ {Keyword, "USER"},
+ {Keyword, "VIEW"},
+ };
+
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"CREATE "}), expected);
+ }
+
+ Y_UNIT_TEST(Delete) {
+ TVector<TCandidate> expected = {
+ {Keyword, "FROM"},
+ };
+
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"DELETE "}), expected);
+ }
+
+ Y_UNIT_TEST(Drop) {
+ TVector<TCandidate> expected = {
+ {Keyword, "ASYNC"},
+ {Keyword, "BACKUP"},
+ {Keyword, "EXTERNAL"},
+ {Keyword, "GROUP"},
+ {Keyword, "OBJECT"},
+ {Keyword, "RESOURCE"},
+ {Keyword, "TABLE"},
+ {Keyword, "TABLESTORE"},
+ {Keyword, "TOPIC"},
+ {Keyword, "TRANSFER"},
+ {Keyword, "USER"},
+ {Keyword, "VIEW"},
+ };
+
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"DROP "}), expected);
+ }
+
+ Y_UNIT_TEST(Explain) {
+ TVector<TCandidate> expected = {
+ {Keyword, "ALTER"},
+ {Keyword, "ANALYZE"},
+ {Keyword, "BACKUP"},
+ {Keyword, "BATCH"},
+ {Keyword, "COMMIT"},
+ {Keyword, "CREATE"},
+ {Keyword, "DECLARE"},
+ {Keyword, "DEFINE"},
+ {Keyword, "DELETE"},
+ {Keyword, "DISCARD"},
+ {Keyword, "DO"},
+ {Keyword, "DROP"},
+ {Keyword, "EVALUATE"},
+ {Keyword, "EXPORT"},
+ {Keyword, "FOR"},
+ {Keyword, "FROM"},
+ {Keyword, "GRANT"},
+ {Keyword, "IF"},
+ {Keyword, "IMPORT"},
+ {Keyword, "INSERT"},
+ {Keyword, "PARALLEL"},
+ {Keyword, "PRAGMA"},
+ {Keyword, "PROCESS"},
+ {Keyword, "QUERY"},
+ {Keyword, "REDUCE"},
+ {Keyword, "REPLACE"},
+ {Keyword, "RESTORE"},
+ {Keyword, "REVOKE"},
+ {Keyword, "ROLLBACK"},
+ {Keyword, "SELECT"},
+ {Keyword, "SHOW"},
+ {Keyword, "UPDATE"},
+ {Keyword, "UPSERT"},
+ {Keyword, "USE"},
+ {Keyword, "VALUES"},
+ };
+
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"EXPLAIN "}), expected);
+ }
+
+ Y_UNIT_TEST(Grant) {
+ TVector<TCandidate> expected = {
+ {Keyword, "ALL"},
+ {Keyword, "ALTER"},
+ {Keyword, "CONNECT"},
+ {Keyword, "CREATE"},
+ {Keyword, "DESCRIBE"},
+ {Keyword, "DROP"},
+ {Keyword, "ERASE"},
+ {Keyword, "FULL"},
+ {Keyword, "GRANT"},
+ {Keyword, "INSERT"},
+ {Keyword, "LIST"},
+ {Keyword, "MANAGE"},
+ {Keyword, "MODIFY"},
+ {Keyword, "REMOVE"},
+ {Keyword, "SELECT"},
+ {Keyword, "UPDATE"},
+ {Keyword, "USE"},
+ };
+
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"GRANT "}), expected);
+ }
+
+ Y_UNIT_TEST(Insert) {
+ TVector<TCandidate> expected = {
+ {Keyword, "INTO"},
+ {Keyword, "OR"},
+ };
+
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"INSERT "}), expected);
+ }
+
+ Y_UNIT_TEST(Pragma) {
+ TVector<TCandidate> expected = {
+ {Keyword, "ANSI"},
+ {Keyword, "CALLABLE"},
+ {Keyword, "DICT"},
+ {Keyword, "ENUM"},
+ {Keyword, "FLOW"},
+ {Keyword, "LIST"},
+ {Keyword, "OPTIONAL"},
+ {Keyword, "RESOURCE"},
+ {Keyword, "SET"},
+ {Keyword, "STRUCT"},
+ {Keyword, "TAGGED"},
+ {Keyword, "TUPLE"},
+ {Keyword, "VARIANT"},
+ };
+
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"PRAGMA "}), expected);
+ }
+
+ Y_UNIT_TEST(Select) {
+ TVector<TCandidate> expected = {
+ {Keyword, "ALL"},
+ {Keyword, "BITCAST"},
+ {Keyword, "CALLABLE"},
+ {Keyword, "CASE"},
+ {Keyword, "CAST"},
+ {Keyword, "CURRENT_DATE"},
+ {Keyword, "CURRENT_TIME"},
+ {Keyword, "CURRENT_TIMESTAMP"},
+ {Keyword, "DICT"},
+ {Keyword, "DISTINCT"},
+ {Keyword, "EMPTY_ACTION"},
+ {Keyword, "ENUM"},
+ {Keyword, "EXISTS"},
+ {Keyword, "FALSE"},
+ {Keyword, "FLOW"},
+ {Keyword, "JSON_EXISTS"},
+ {Keyword, "JSON_QUERY"},
+ {Keyword, "JSON_VALUE"},
+ {Keyword, "LIST"},
+ {Keyword, "NOT"},
+ {Keyword, "NULL"},
+ {Keyword, "OPTIONAL"},
+ {Keyword, "RESOURCE"},
+ {Keyword, "SET"},
+ {Keyword, "STREAM"},
+ {Keyword, "STRUCT"},
+ {Keyword, "TAGGED"},
+ {Keyword, "TRUE"},
+ {Keyword, "TUPLE"},
+ {Keyword, "VARIANT"},
+ };
+
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"SELECT "}), expected);
+ }
+
+ Y_UNIT_TEST(Upsert) {
+ TVector<TCandidate> expected = {
+ {Keyword, "INTO"},
+ {Keyword, "OBJECT"},
+ };
+
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"UPSERT "}), expected);
+ }
+
+ Y_UNIT_TEST(UTF8Wide) {
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"\xF0\x9F\x98\x8A"}).size(), 0);
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"编码"}).size(), 0);
+ }
+
+ Y_UNIT_TEST(WordBreak) {
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"SELECT ("}).size(), 28);
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"SELECT (1)"}).size(), 30);
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, {"SELECT 1;"}).size(), 35);
+ }
+
+ Y_UNIT_TEST(Typing) {
+ const auto queryUtf16 = TUtf16String::FromUtf8(
+ "SELECT \n"
+ " 123467, \"Hello, {name}! 编码\"}, \n"
+ " (1 + (5 * 1 / 0)), MIN(identifier), \n"
+ " Bool(field), Math::Sin(var) \n"
+ "FROM `local/test/space/table` JOIN test;");
+
+ auto engine = MakeSqlCompletionEngine();
+
+ for (std::size_t size = 0; size <= queryUtf16.size(); ++size) {
+ const TWtringBuf prefixUtf16(queryUtf16, 0, size);
+ auto completion = engine->Complete({TString::FromUtf16(prefixUtf16)});
+ Y_DO_NOT_OPTIMIZE_AWAY(completion);
+ }
+ }
+
+ Y_UNIT_TEST(CaseInsensitivity) {
+ TVector<TCandidate> expected = {
+ {Keyword, "SELECT"},
+ };
+
+ auto engine = MakeSqlCompletionEngine();
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "se"), expected);
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "sE"), expected);
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "Se"), expected);
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "SE"), expected);
+ }
+} // Y_UNIT_TEST_SUITE(SqlCompleteTests)
diff --git a/yql/essentials/sql/v1/complete/sql_context.cpp b/yql/essentials/sql/v1/complete/sql_context.cpp
new file mode 100644
index 0000000000..18f676e40b
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/sql_context.cpp
@@ -0,0 +1,123 @@
+#include "sql_context.h"
+
+#include "c3_engine.h"
+#include "sql_syntax.h"
+
+#include <yql/essentials/parser/antlr_ast/gen/v1_antlr4/SQLv1Antlr4Lexer.h>
+#include <yql/essentials/parser/antlr_ast/gen/v1_antlr4/SQLv1Antlr4Parser.h>
+#include <yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/SQLv1Antlr4Lexer.h>
+#include <yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4/SQLv1Antlr4Parser.h>
+
+#include <util/generic/algorithm.h>
+#include <util/stream/output.h>
+
+namespace NSQLComplete {
+
+ template <bool IsAnsiLexer>
+ class TSpecializedSqlContextInference: public ISqlContextInference {
+ private:
+ using TDefaultYQLGrammar = TAntlrGrammar<
+ NALADefaultAntlr4::SQLv1Antlr4Lexer,
+ NALADefaultAntlr4::SQLv1Antlr4Parser>;
+
+ using TAnsiYQLGrammar = TAntlrGrammar<
+ NALAAnsiAntlr4::SQLv1Antlr4Lexer,
+ NALAAnsiAntlr4::SQLv1Antlr4Parser>;
+
+ using G = std::conditional_t<
+ IsAnsiLexer,
+ TAnsiYQLGrammar,
+ TDefaultYQLGrammar>;
+
+ public:
+ TSpecializedSqlContextInference()
+ : Grammar(&GetSqlGrammar(IsAnsiLexer))
+ , C3(ComputeC3Config())
+ {
+ }
+
+ TCompletionContext Analyze(TCompletionInput input) override {
+ auto prefix = input.Text.Head(input.CursorPosition);
+ auto tokens = C3.Complete(prefix);
+ FilterIdKeywords(tokens);
+ return {
+ .Keywords = SiftedKeywords(tokens),
+ };
+ }
+
+ private:
+ IC3Engine::TConfig ComputeC3Config() {
+ return {
+ .IgnoredTokens = ComputeIgnoredTokens(),
+ .PreferredRules = ComputePreferredRules(),
+ };
+ }
+
+ std::unordered_set<TTokenId> ComputeIgnoredTokens() {
+ auto ignoredTokens = Grammar->GetAllTokens();
+ for (auto keywordToken : Grammar->GetKeywordTokens()) {
+ ignoredTokens.erase(keywordToken);
+ }
+ return ignoredTokens;
+ }
+
+ std::unordered_set<TRuleId> ComputePreferredRules() {
+ const auto& keywordRules = Grammar->GetKeywordRules();
+
+ std::unordered_set<TRuleId> preferredRules;
+ preferredRules.insert(std::begin(keywordRules), std::end(keywordRules));
+ return preferredRules;
+ }
+
+ void FilterIdKeywords(TVector<TSuggestedToken>& tokens) {
+ const auto& keywordRules = Grammar->GetKeywordRules();
+ auto [first, last] = std::ranges::remove_if(tokens, [&](const TSuggestedToken& token) {
+ return AnyOf(token.ParserCallStack, [&](TRuleId rule) {
+ return Find(keywordRules, rule) != std::end(keywordRules);
+ });
+ });
+ tokens.erase(first, last);
+ }
+
+ TVector<TString> SiftedKeywords(const TVector<TSuggestedToken>& tokens) {
+ const auto& vocabulary = Grammar->GetVocabulary();
+ const auto& keywordTokens = Grammar->GetKeywordTokens();
+
+ TVector<TString> keywords;
+ for (const auto& token : tokens) {
+ if (keywordTokens.contains(token.Number)) {
+ keywords.emplace_back(vocabulary.getDisplayName(token.Number));
+ }
+ }
+ return keywords;
+ }
+
+ const ISqlGrammar* Grammar;
+ TC3Engine<G> C3;
+ };
+
+ class TSqlContextInference: public ISqlContextInference {
+ public:
+ TCompletionContext Analyze(TCompletionInput input) override {
+ auto isAnsiLexer = IsAnsiQuery(TString(input.Text));
+ auto& engine = GetSpecializedEngine(isAnsiLexer);
+ return engine.Analyze(std::move(input));
+ }
+
+ private:
+ ISqlContextInference& GetSpecializedEngine(bool isAnsiLexer) {
+ if (isAnsiLexer) {
+ return AnsiEngine;
+ }
+ return DefaultEngine;
+ }
+
+ TSpecializedSqlContextInference</* IsAnsiLexer = */ false> DefaultEngine;
+ TSpecializedSqlContextInference</* IsAnsiLexer = */ true> AnsiEngine;
+ };
+
+ ISqlContextInference::TPtr MakeSqlContextInference() {
+ return TSqlContextInference::TPtr(new TSqlContextInference());
+ }
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/sql_context.h b/yql/essentials/sql/v1/complete/sql_context.h
new file mode 100644
index 0000000000..bc3b8d4840
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/sql_context.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "sql_complete.h"
+
+#include <util/generic/string.h>
+
+namespace NSQLComplete {
+
+ struct TCompletionContext {
+ TVector<TString> Keywords;
+ };
+
+ class ISqlContextInference {
+ public:
+ using TPtr = THolder<ISqlContextInference>;
+
+ virtual TCompletionContext Analyze(TCompletionInput input) = 0;
+ virtual ~ISqlContextInference() = default;
+ };
+
+ ISqlContextInference::TPtr MakeSqlContextInference();
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/sql_syntax.cpp b/yql/essentials/sql/v1/complete/sql_syntax.cpp
new file mode 100644
index 0000000000..ba5a08d371
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/sql_syntax.cpp
@@ -0,0 +1,19 @@
+#include "sql_syntax.h"
+
+#include <yql/essentials/public/issue/yql_issue.h>
+#include <yql/essentials/sql/settings/translation_settings.h>
+
+namespace NSQLComplete {
+
+ using NSQLTranslation::ParseTranslationSettings;
+ using NSQLTranslation::TTranslationSettings;
+ using NYql::TIssues;
+
+ bool IsAnsiQuery(const TString& query) {
+ TTranslationSettings settings;
+ TIssues issues;
+ ParseTranslationSettings(query, settings, issues);
+ return settings.AnsiLexer;
+ }
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/sql_syntax.h b/yql/essentials/sql/v1/complete/sql_syntax.h
new file mode 100644
index 0000000000..f03cbc9fb9
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/sql_syntax.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <util/generic/fwd.h>
+
+namespace NSQLComplete {
+
+ // Permits invalid special comments
+ bool IsAnsiQuery(const TString& query);
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/string_util.cpp b/yql/essentials/sql/v1/complete/string_util.cpp
new file mode 100644
index 0000000000..12a6701065
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/string_util.cpp
@@ -0,0 +1,29 @@
+#include "string_util.h"
+
+#include <util/generic/strbuf.h>
+
+namespace NSQLComplete {
+
+ bool IsWordBoundary(char ch) { // Is optimized into table lookup by clang
+ for (size_t i = 0; i < sizeof(WordBreakCharacters) - 1; ++i) {
+ if (WordBreakCharacters[i] == ch) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ size_t LastWordIndex(TStringBuf text) {
+ for (auto it = std::rbegin(text); it != std::rend(text); std::advance(it, 1)) {
+ if (IsWordBoundary(*it)) {
+ return std::distance(it, std::rend(text));
+ }
+ }
+ return 0;
+ }
+
+ TStringBuf LastWord(TStringBuf text) {
+ return text.SubStr(LastWordIndex(text));
+ }
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/string_util.h b/yql/essentials/sql/v1/complete/string_util.h
new file mode 100644
index 0000000000..bafc578d82
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/string_util.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <util/charset/unidata.h>
+
+#include <string_view>
+
+namespace NSQLComplete {
+
+ static const char WordBreakCharacters[] = " \t\v\f\a\b\r\n`~!@#$%^&*-=+[](){}\\|;:'\".,<>/?";
+
+ bool IsWordBoundary(char ch);
+
+ size_t LastWordIndex(TStringBuf text);
+
+ TStringBuf LastWord(TStringBuf text);
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/string_util_ut.cpp b/yql/essentials/sql/v1/complete/string_util_ut.cpp
new file mode 100644
index 0000000000..ca3ed546a3
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/string_util_ut.cpp
@@ -0,0 +1,21 @@
+#include "string_util.h"
+
+#include <library/cpp/testing/unittest/registar.h>
+
+using namespace NSQLComplete;
+
+Y_UNIT_TEST_SUITE(StringUtilTest) {
+ Y_UNIT_TEST(Blank) {
+ UNIT_ASSERT_VALUES_EQUAL(LastWord(""), "");
+ UNIT_ASSERT_VALUES_EQUAL(LastWord(" "), "");
+ UNIT_ASSERT_VALUES_EQUAL(LastWord(" "), "");
+ UNIT_ASSERT_VALUES_EQUAL(LastWord(" "), "");
+ }
+
+ Y_UNIT_TEST(Space) {
+ UNIT_ASSERT_VALUES_EQUAL(LastWord("two "), "");
+ UNIT_ASSERT_VALUES_EQUAL(LastWord("one two "), "");
+ UNIT_ASSERT_VALUES_EQUAL(LastWord("two"), "two");
+ UNIT_ASSERT_VALUES_EQUAL(LastWord("one two"), "two");
+ }
+} // Y_UNIT_TEST_SUITE(StringUtilTest)
diff --git a/yql/essentials/sql/v1/complete/ut/ya.make b/yql/essentials/sql/v1/complete/ut/ya.make
new file mode 100644
index 0000000000..91f7da1361
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/ut/ya.make
@@ -0,0 +1,8 @@
+UNITTEST_FOR(yql/essentials/sql/v1/complete)
+
+SRCS(
+ sql_complete_ut.cpp
+ string_util_ut.cpp
+)
+
+END()
diff --git a/yql/essentials/sql/v1/complete/ya.make b/yql/essentials/sql/v1/complete/ya.make
new file mode 100644
index 0000000000..70189e5f50
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/ya.make
@@ -0,0 +1,24 @@
+LIBRARY()
+
+SRCS(
+ sql_antlr4.cpp
+ sql_complete.cpp
+ sql_context.cpp
+ sql_syntax.cpp
+ string_util.cpp
+)
+
+PEERDIR(
+ contrib/libs/antlr4_cpp_runtime
+ contrib/libs/antlr4-c3
+ yql/essentials/sql/settings
+ yql/essentials/sql/v1/format
+ yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4
+ yql/essentials/parser/antlr_ast/gen/v1_antlr4
+)
+
+END()
+
+RECURSE_FOR_TESTS(
+ ut
+)
diff --git a/yql/essentials/sql/v1/sql_translation.cpp b/yql/essentials/sql/v1/sql_translation.cpp
index 71dfafb3c4..af492431c8 100644
--- a/yql/essentials/sql/v1/sql_translation.cpp
+++ b/yql/essentials/sql/v1/sql_translation.cpp
@@ -3819,35 +3819,44 @@ bool TSqlTranslation::RoleNameClause(const TRule_role_name& node, TDeferredAtom&
}
bool TSqlTranslation::PasswordParameter(const TRule_password_option& passwordOption, TUserParameters& result) {
- // password_option: ENCRYPTED? PASSWORD expr;
- TSqlExpression expr(Ctx, Mode);
- TNodePtr password = expr.Build(passwordOption.GetRule_expr3());
- if (!password) {
- Error() << "Couldn't parse the password";
- return false;
+ // password_option: ENCRYPTED? PASSWORD password_value;
+ // password_value: STRING_VALUE | NULL;
+
+ const auto& token = passwordOption.GetRule_password_value3().GetToken1();
+ TString stringValue(Ctx.Token(token));
+
+ if (to_lower(stringValue) == "null") {
+ // result.Password = default value
+ } else {
+ auto password = StringContent(Ctx, Ctx.Pos(), stringValue);
+
+ if (!password) {
+ Error() << "Password should be enclosed into quotation marks.";
+ return false;
+ }
+
+ result.Password = TDeferredAtom(Ctx.Pos(), std::move(password->Content));
}
result.IsPasswordEncrypted = passwordOption.HasBlock1();
- if (!password->IsNull()) {
- result.Password = MakeAtomFromExpression(Ctx.Pos(), Ctx, password);
- }
return true;
}
bool TSqlTranslation::HashParameter(const TRule_hash_option& hashOption, TUserParameters& result) {
- // hash_option: HASH expr;
- TSqlExpression expr(Ctx, Mode);
- TNodePtr hash = expr.Build(hashOption.GetRule_expr2());
+ // hash_option: HASH STRING_VALUE;
+
+ const auto& token = hashOption.GetToken2();
+ TString stringValue(Ctx.Token(token));
+
+ auto hash = StringContent(Ctx, Ctx.Pos(), stringValue);
if (!hash) {
- Error() << "Couldn't parse the hash of password";
+ Error() << "Hash should be enclosed into quotation marks.";
return false;
}
- if (!hash->IsNull()) {
- result.Hash = MakeAtomFromExpression(Ctx.Pos(), Ctx, hash);
- }
+ result.Hash = TDeferredAtom(Ctx.Pos(), std::move(hash->Content));
return true;
}
diff --git a/yql/essentials/sql/v1/sql_ut.cpp b/yql/essentials/sql/v1/sql_ut.cpp
index aa1e17e53f..66c9a159fb 100644
--- a/yql/essentials/sql/v1/sql_ut.cpp
+++ b/yql/essentials/sql/v1/sql_ut.cpp
@@ -4,7 +4,6 @@
#include <yql/essentials/providers/common/provider/yql_provider_names.h>
#include <yql/essentials/sql/sql.h>
-#include <yql/essentials/sql/v1/sql.h>
#include <yql/essentials/sql/v1/lexer/antlr3/lexer.h>
#include <util/generic/map.h>
@@ -30,8121 +29,10 @@ TParsedTokenList Tokenize(const TString& query) {
return tokens;
}
-TString ToString(const TParsedTokenList& tokens) {
- TStringBuilder reconstructedQuery;
- for (const auto& token : tokens) {
- if (token.Name == "WS" || token.Name == "EOF") {
- continue;
- }
- if (!reconstructedQuery.empty()) {
- reconstructedQuery << ' ';
- }
- reconstructedQuery << token.Content;
- }
- return reconstructedQuery;
-}
-
-}
-
-Y_UNIT_TEST_SUITE(AnsiMode) {
- Y_UNIT_TEST(PragmaAnsi) {
- UNIT_ASSERT(SqlToYql("PRAGMA ANSI 2016;").IsOk());
- }
-}
-
-Y_UNIT_TEST_SUITE(SqlParsingOnly) {
- ///This function is used in BACKWARD COMPATIBILITY tests below that LIMIT the sets of token that CAN NOT be used
- ///as identifiers in different contexts in a SQL request
- ///\return list of tokens that failed this check
- TVector<TString> ValidateTokens(const THashSet<TString>& forbidden, const std::function<TString (const TString& )>& makeRequest) {
- THashMap<TString, bool> allTokens;
- for (const auto& t: NSQLFormat::GetKeywords()) {
- allTokens[t] = !forbidden.contains((t));
- }
- for (const auto& f: forbidden) {
- UNIT_ASSERT(allTokens.contains(f)); //check that forbidden list contains tokens only(argument check)
- }
- TVector<TString> failed;
- for (const auto& [token, allowed]: allTokens) {
- if (SqlToYql(makeRequest(token)).IsOk() != allowed)
- failed.push_back(token);
- }
- return failed;
- }
-
- Y_UNIT_TEST(TokensAsColumnName) { //id_expr
- auto failed = ValidateTokens({
- "ALL", "ANY", "AS", "ASSUME", "ASYMMETRIC", "AUTOMAP", "BETWEEN", "BITCAST",
- "CALLABLE", "CASE", "CAST", "CUBE", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
- "DICT", "DISTINCT", "ENUM", "ERASE", "EXCEPT", "EXISTS", "FLOW", "FROM", "FULL",
- "HAVING", "HOP", "INTERSECT", "JSON_EXISTS", "JSON_QUERY", "JSON_VALUE", "LIMIT", "LIST",
- "NOT", "OPTIONAL", "PROCESS", "REDUCE", "REPEATABLE", "RESOURCE", "RETURN", "RETURNING", "ROLLUP",
- "SELECT", "SET", "STREAM", "STRUCT", "SYMMETRIC", "TAGGED", "TUPLE", "UNBOUNDED",
- "UNION", "VARIANT", "WHEN", "WHERE", "WINDOW", "WITHOUT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT " << token << " FROM Plato.Input";
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsWithoutColumnName) { //id_without
- auto failed = ValidateTokens({
- "ALL", "AS", "ASSUME", "ASYMMETRIC", "AUTOMAP", "BETWEEN", "BITCAST",
- "CALLABLE", "CASE", "CAST", "CUBE", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
- "DICT", "DISTINCT", "EMPTY_ACTION", "ENUM", "EXCEPT", "EXISTS", "FALSE", "FLOW", "FROM", "FULL",
- "HAVING", "HOP", "INTERSECT", "JSON_EXISTS", "JSON_QUERY", "JSON_VALUE", "LIMIT", "LIST",
- "NOT", "NULL", "OPTIONAL", "PROCESS", "REDUCE", "REPEATABLE", "RESOURCE", "RETURN", "RETURNING", "ROLLUP",
- "SELECT", "SET", "STRUCT", "SYMMETRIC", "TAGGED", "TRUE", "TUPLE", "UNBOUNDED",
- "UNION", "VARIANT", "WHEN", "WHERE", "WINDOW", "WITHOUT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT * WITHOUT " << token << " FROM Plato.Input";
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsColumnNameInAddColumn) { //id_schema
- auto failed = ValidateTokens({
- "ANY", "AUTOMAP", "CALLABLE", "COLUMN", "DICT", "ENUM", "ERASE", "FALSE", "FLOW",
- "LIST", "OPTIONAL", "REPEATABLE", "RESOURCE",
- "SET", "STREAM", "STRUCT", "TAGGED", "TRUE", "TUPLE", "VARIANT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "ALTER TABLE Plato.Input ADD COLUMN " << token << " Bool";
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsColumnAlias) {
- auto failed = ValidateTokens({
- "AUTOMAP", "FALSE",
- "REPEATABLE", "TRUE"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT Col as " << token << " FROM Plato.Input";
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsTableName) { //id_table_or_type
- auto failed = ValidateTokens({
- "ANY", "AUTOMAP", "COLUMN", "ERASE", "FALSE",
- "REPEATABLE", "STREAM", "TRUE"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT * FROM Plato." << token;
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsTableAlias) { //id_table
- auto failed = ValidateTokens({
- "AUTOMAP", "CALLABLE", "DICT", "ENUM","FALSE", "FLOW",
- "LIST", "OPTIONAL", "REPEATABLE", "RESOURCE",
- "SET", "STRUCT", "TAGGED", "TRUE", "TUPLE", "VARIANT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT * FROM Plato.Input AS " << token;
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsHints) { //id_hint
- auto failed = ValidateTokens({
- "AUTOMAP", "CALLABLE", "COLUMNS", "DICT", "ENUM", "FALSE", "FLOW",
- "LIST", "OPTIONAL", "REPEATABLE", "RESOURCE",
- "SCHEMA", "SET", "STRUCT", "TAGGED", "TRUE", "TUPLE", "VARIANT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT * FROM Plato.Input WITH " << token;
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsWindow) { //id_window
- auto failed = ValidateTokens({
- "AUTOMAP", "CALLABLE", "DICT", "ENUM", "FALSE", "FLOW", "GROUPS", "LIST", "OPTIONAL",
- "RANGE", "REPEATABLE", "RESOURCE", "ROWS", "SET", "STRUCT", "TAGGED" ,"TRUE", "TUPLE", "VARIANT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT * FROM Plato.Input WINDOW " << token << " AS ()";
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsIdExprIn) { //id_expr_in
- auto failed = ValidateTokens({
- "ALL", "ANY", "AS", "ASSUME", "ASYMMETRIC", "AUTOMAP", "BETWEEN", "BITCAST",
- "CALLABLE", "CASE", "CAST", "COMPACT", "CUBE", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
- "DICT", "DISTINCT", "ENUM", "ERASE", "EXCEPT", "EXISTS", "FLOW", "FROM", "FULL",
- "HAVING", "HOP", "INTERSECT", "JSON_EXISTS", "JSON_QUERY", "JSON_VALUE", "LIMIT", "LIST",
- "NOT", "OPTIONAL", "PROCESS", "REDUCE", "REPEATABLE", "RESOURCE", "RETURN", "RETURNING", "ROLLUP",
- "SELECT", "SET", "STREAM", "STRUCT", "SYMMETRIC", "TAGGED", "TUPLE", "UNBOUNDED",
- "UNION", "VARIANT", "WHEN", "WHERE", "WINDOW", "WITHOUT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT * FROM Plato.Input WHERE q IN " << token;
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TableHints) {
- UNIT_ASSERT(SqlToYql("SELECT * FROM plato.Input WITH INFER_SCHEMA").IsOk());
- UNIT_ASSERT(SqlToYql("SELECT * FROM plato.Input WITH (INFER_SCHEMA)").IsOk());
- }
-
- Y_UNIT_TEST(InNoHints) {
- TString query = "SELECT * FROM plato.Input WHERE key IN (1,2,3)";
-
- VerifySqlInHints(query, { "'('('warnNoAnsi))" }, {});
- VerifySqlInHints(query, { "'()" }, false);
- VerifySqlInHints(query, { "'('('ansi))" }, true);
- }
-
- Y_UNIT_TEST(InHintCompact) {
- // should parse COMPACT as hint
- TString query = "SELECT * FROM plato.Input WHERE key IN COMPACT(1, 2, 3)";
-
- VerifySqlInHints(query, { "'('isCompact)" });
- }
-
- Y_UNIT_TEST(InHintSubquery) {
- // should parse tableSource as hint
- TString query = "$subq = (SELECT key FROM plato.Input); SELECT * FROM plato.Input WHERE key IN $subq";
-
- VerifySqlInHints(query, { "'('tableSource)" });
- }
-
- Y_UNIT_TEST(InHintCompactSubquery) {
- TString query = "$subq = (SELECT key FROM plato.Input); SELECT * FROM plato.Input WHERE key IN COMPACT $subq";
-
- VerifySqlInHints(query, { "'('isCompact)", "'('tableSource)" });
- }
-
- Y_UNIT_TEST(CompactKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("SELECT COMPACT FROM plato.Input WHERE COMPACT IN COMPACT(1, 2, 3)").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT * FROM COMPACT").IsOk());
- }
-
- Y_UNIT_TEST(FamilyKeywordNotReservedForNames) {
- // FIXME: check if we can get old behaviour
- //UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE FAMILY (FAMILY Uint32, PRIMARY KEY (FAMILY));").IsOk());
- //UNIT_ASSERT(SqlToYql("USE plato; SELECT FAMILY FROM FAMILY").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT FAMILY FROM Input").IsOk());
- }
-
- Y_UNIT_TEST(ResetKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE RESET (RESET Uint32, PRIMARY KEY (RESET));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT RESET FROM RESET").IsOk());
- }
-
- Y_UNIT_TEST(SyncKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE SYNC (SYNC Uint32, PRIMARY KEY (SYNC));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT SYNC FROM SYNC").IsOk());
- }
-
- Y_UNIT_TEST(AsyncKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE ASYNC (ASYNC Uint32, PRIMARY KEY (ASYNC));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT ASYNC FROM ASYNC").IsOk());
- }
-
- Y_UNIT_TEST(DisableKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE DISABLE (DISABLE Uint32, PRIMARY KEY (DISABLE));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT DISABLE FROM DISABLE").IsOk());
- }
-
- Y_UNIT_TEST(ChangefeedKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE CHANGEFEED (CHANGEFEED Uint32, PRIMARY KEY (CHANGEFEED));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT CHANGEFEED FROM CHANGEFEED").IsOk());
- }
-
- Y_UNIT_TEST(ReplicationKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE REPLICATION (REPLICATION Uint32, PRIMARY KEY (REPLICATION));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT REPLICATION FROM REPLICATION").IsOk());
- }
-
- Y_UNIT_TEST(TransferKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE TRANSFER (TRANSFER Uint32, PRIMARY KEY (TRANSFER));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT TRANSFER FROM TRANSFER").IsOk());
- }
-
- Y_UNIT_TEST(SecondsKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE SECONDS (SECONDS Uint32, PRIMARY KEY (SECONDS));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT SECONDS FROM SECONDS").IsOk());
- }
-
- Y_UNIT_TEST(MillisecondsKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE MILLISECONDS (MILLISECONDS Uint32, PRIMARY KEY (MILLISECONDS));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT MILLISECONDS FROM MILLISECONDS").IsOk());
- }
-
- Y_UNIT_TEST(MicrosecondsKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE MICROSECONDS (MICROSECONDS Uint32, PRIMARY KEY (MICROSECONDS));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT MICROSECONDS FROM MICROSECONDS").IsOk());
- }
-
- Y_UNIT_TEST(NanosecondsKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE NANOSECONDS (NANOSECONDS Uint32, PRIMARY KEY (NANOSECONDS));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT NANOSECONDS FROM NANOSECONDS").IsOk());
- }
-
- Y_UNIT_TEST(Jubilee) {
- NYql::TAstParseResult res = SqlToYql("USE plato; INSERT INTO Arcadia (r2000000) VALUES (\"2M GET!!!\");");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(QualifiedAsteriskBefore) {
- NYql::TAstParseResult res = SqlToYql(
- "PRAGMA DisableSimpleColumns;"
- "select interested_table.*, LENGTH(value) AS megahelpful_len from plato.Input as interested_table;"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- static bool seenStar = false;
- if (word == "FlattenMembers") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("interested_table."));
- } else if (word == "SqlProjectItem") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("megahelpful_len")));
- UNIT_ASSERT_VALUES_EQUAL(seenStar, true);
- } else if (word == "SqlProjectStarItem") {
- seenStar = true;
- }
- };
- TWordCountHive elementStat = {{TString("FlattenMembers"), 0}, {TString("SqlProjectItem"), 0}, {TString("SqlProjectStarItem"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["FlattenMembers"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectStarItem"]);
- }
-
- Y_UNIT_TEST(QualifiedAsteriskAfter) {
- NYql::TAstParseResult res = SqlToYql(
- "PRAGMA DisableSimpleColumns;"
- "select LENGTH(value) AS megahelpful_len, interested_table.* from plato.Input as interested_table;"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- static bool seenStar = false;
- if (word == "FlattenMembers") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("interested_table."));
- } else if (word == "SqlProjectItem") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("megahelpful_len")));
- UNIT_ASSERT_VALUES_EQUAL(seenStar, false);
- } else if (word == "SqlProjectStarItem") {
- seenStar = true;
- }
- };
- TWordCountHive elementStat = {{TString("FlattenMembers"), 0}, {TString("SqlProjectItem"), 0}, {TString("SqlProjectStarItem"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["FlattenMembers"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectStarItem"]);
- }
-
- Y_UNIT_TEST(QualifiedMembers) {
- NYql::TAstParseResult res = SqlToYql("select interested_table.key, interested_table.value from plato.Input as interested_table;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- const bool fieldKey = TString::npos != line.find(Quote("key"));
- const bool fieldValue = TString::npos != line.find(Quote("value"));
- const bool refOnTable = TString::npos != line.find("interested_table.");
- if (word == "SqlProjectItem") {
- UNIT_ASSERT(fieldKey || fieldValue);
- UNIT_ASSERT(!refOnTable);
- } else if (word == "Write!") {
- UNIT_ASSERT(fieldKey && fieldValue && !refOnTable);
- }
- };
- TWordCountHive elementStat = {{TString("SqlProjectStarItem"), 0}, {TString("SqlProjectItem"), 0}, {TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(0, elementStat["SqlProjectStarItem"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(ExplainQueryPlan) {
- UNIT_ASSERT(SqlToYql("EXPLAIN SELECT 1;").IsOk());
- UNIT_ASSERT(SqlToYql("EXPLAIN QUERY PLAN SELECT 1;").IsOk());
- }
-
- Y_UNIT_TEST(JoinParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- "PRAGMA DisableSimpleColumns;"
- " SELECT table_bb.*, table_aa.key as megakey"
- " FROM plato.Input AS table_aa"
- " JOIN plato.Input AS table_bb"
- " ON table_aa.value == table_bb.value;"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "SelectMembers") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("table_aa."));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table_bb."));
- } else if (word == "SqlProjectItem") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("megakey")));
- } else if (word == "SqlColumn") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("table_aa")));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("key")));
- }
- };
- TWordCountHive elementStat = {{TString("SqlProjectItem"), 0}, {TString("SqlProjectStarItem"), 0}, {TString("SelectMembers"), 0}, {TString("SqlColumn"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectStarItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SelectMembers"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlColumn"]);
- }
-
- Y_UNIT_TEST(Join3Table) {
- NYql::TAstParseResult res = SqlToYql(
- " PRAGMA DisableSimpleColumns;"
- " SELECT table_bb.*, table_aa.key as gigakey, table_cc.* "
- " FROM plato.Input AS table_aa"
- " JOIN plato.Input AS table_bb ON table_aa.key == table_bb.key"
- " JOIN plato.Input AS table_cc ON table_aa.subkey == table_cc.subkey;"
- );
- Err2Str(res);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "SelectMembers") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("table_aa."));
- UNIT_ASSERT(line.find("table_bb.") != TString::npos || line.find("table_cc.") != TString::npos);
- } else if (word == "SqlProjectItem") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("gigakey")));
- } else if (word == "SqlColumn") {
- const auto posTableAA = line.find(Quote("table_aa"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, posTableAA);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("key")));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("table_aa", posTableAA + 3));
- }
- };
- TWordCountHive elementStat = {{TString("SqlProjectItem"), 0}, {TString("SqlProjectStarItem"), 0}, {TString("SelectMembers"), 0}, {TString("SqlColumn"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlProjectStarItem"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SelectMembers"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlColumn"]);
- }
-
- Y_UNIT_TEST(DisabledJoinCartesianProduct) {
- NYql::TAstParseResult res = SqlToYql("pragma DisableAnsiImplicitCrossJoin; use plato; select * from A,B,C");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:67: Error: Cartesian product of tables is disabled. Please use explicit CROSS JOIN or enable it via PRAGMA AnsiImplicitCrossJoin\n");
- }
-
- Y_UNIT_TEST(JoinCartesianProduct) {
- NYql::TAstParseResult res = SqlToYql("pragma AnsiImplicitCrossJoin; use plato; select * from A,B,C");
- UNIT_ASSERT(res.Root);
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "EquiJoin") {
- auto pos = line.find("Cross");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, pos);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Cross", pos + 1));
- }
- };
- TWordCountHive elementStat = {{TString("EquiJoin"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["EquiJoin"]);
- }
-
- Y_UNIT_TEST(JoinWithoutConcreteColumns) {
- NYql::TAstParseResult res = SqlToYql(
- " use plato;"
- " SELECT a.v, b.value"
- " FROM `Input1` VIEW `ksv` AS a"
- " JOIN `Input2` AS b"
- " ON a.k == b.key;"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "SqlProjectItem") {
- UNIT_ASSERT(line.find(Quote("a.v")) != TString::npos || line.find(Quote("b.value")) != TString::npos);
- } else if (word == "SqlColumn") {
- const auto posTableA = line.find(Quote("a"));
- const auto posTableB = line.find(Quote("b"));
- if (posTableA != TString::npos) {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("v")));
- } else {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, posTableB);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("value")));
- }
- }
- };
- TWordCountHive elementStat = {{TString("SqlProjectStarItem"), 0}, {TString("SqlProjectItem"), 0}, {TString("SqlColumn"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(0, elementStat["SqlProjectStarItem"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlColumn"]);
- }
-
- Y_UNIT_TEST(JoinWithSameValues) {
- NYql::TAstParseResult res = SqlToYql("SELECT a.value, b.value FROM plato.Input AS a JOIN plato.Input as b ON a.key == b.key;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "SqlProjectItem") {
- const bool isValueFromA = TString::npos != line.find(Quote("a.value"));
- const bool isValueFromB = TString::npos != line.find(Quote("b.value"));
- UNIT_ASSERT(isValueFromA || isValueFromB);
- } if (word == "Write!") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("a.a."));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("b.b."));
- }
- };
- TWordCountHive elementStat = {{TString("SqlProjectStarItem"), 0}, {TString("SqlProjectItem"), 0}, {"Write!", 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(0, elementStat["SqlProjectStarItem"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(SameColumnsForDifferentTables) {
- NYql::TAstParseResult res = SqlToYql("SELECT a.key, b.key FROM plato.Input as a JOIN plato.Input as b on a.key==b.key;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SameColumnsForDifferentTablesFullJoin) {
- NYql::TAstParseResult res = SqlToYql("SELECT a.key, b.key, a.value, b.value FROM plato.Input AS a FULL JOIN plato.Input AS b USING(key);");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(JoinStreamLookupStrategyHint) {
- {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ StreamLookup() */ plato.Input AS b USING(key);");
- UNIT_ASSERT(res.Root);
- }
- //case insensitive
- {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ streamlookup() */ plato.Input AS b USING(key);");
- UNIT_ASSERT(res.Root);
- }
- }
-
- Y_UNIT_TEST(JoinConflictingStrategyHint) {
- {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ StreamLookup() */ /*+ Merge() */ plato.Input AS b USING(key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:91: Error: Conflicting join strategy hints\n");
- }
- }
-
- Y_UNIT_TEST(JoinDuplicatingStrategyHint) {
- {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ StreamLookup() */ /*+ StreamLookup() */ plato.Input AS b USING(key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:98: Error: Duplicate join strategy hint\n");
- }
- }
-
- Y_UNIT_TEST(WarnCrossJoinStrategyHint) {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a CROSS JOIN /*+ merge() */ plato.Input AS b;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:32: Warning: Non-default join strategy will not be used for CROSS JOIN, code: 4534\n");
- }
-
- Y_UNIT_TEST(WarnCartesianProductStrategyHint) {
- NYql::TAstParseResult res = SqlToYql("pragma AnsiImplicitCrossJoin; use plato; SELECT * FROM A, /*+ merge() */ B;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:74: Warning: Non-default join strategy will not be used for CROSS JOIN, code: 4534\n");
- }
-
- Y_UNIT_TEST(WarnUnknownJoinStrategyHint) {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ xmerge() */ plato.Input AS b USING (key);");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:41: Warning: Unsupported join hint: xmerge, code: 4534\n");
- }
-
- Y_UNIT_TEST(ReverseLabels) {
- NYql::TAstParseResult res = SqlToYql("select in.key as subkey, subkey as key from plato.Input as in;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(AutogenerationAliasWithoutCollisionConflict1) {
- NYql::TAstParseResult res = SqlToYql("select LENGTH(Value), key as column1 from plato.Input;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(AutogenerationAliasWithoutCollision2Conflict2) {
- NYql::TAstParseResult res = SqlToYql("select key as column0, LENGTH(Value) from plato.Input;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(InputAliasForQualifiedAsterisk) {
- NYql::TAstParseResult res = SqlToYql("use plato; select zyuzya.*, key from plato.Input as zyuzya;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectSupportsResultColumnsWithTrailingComma) {
- NYql::TAstParseResult res = SqlToYql("select a, b, c, from plato.Input;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectOrderByLabeledColumn) {
- NYql::TAstParseResult res = SqlToYql("pragma DisableOrderedColumns; select key as goal from plato.Input order by goal");
- UNIT_ASSERT(res.Root);
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "DataSource") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("plato"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Input"));
-
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("goal"));
- } else if (word == "Sort") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("goal"));
-
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("key"));
- }
- };
- TWordCountHive elementStat = {{TString("DataSource"), 0}, {TString("Sort"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["DataSource"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Sort"]);
- }
-
- Y_UNIT_TEST(SelectOrderBySimpleExpr) {
- NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by a + a");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectAssumeOrderByTableRowAccess) {
- NYql::TAstParseResult res = SqlToYql("$key = 'foo';select * from plato.Input assume order by TableRow().$key");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectOrderByDuplicateLabels) {
- NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by a, a");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectOrderByExpression) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input as i order by cast(key as uint32) + cast(subkey as uint32)");
- UNIT_ASSERT(res.Root);
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Sort") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"+MayWarn\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("key"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("subkey"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Bool 'true)"));
-
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("i.key"));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("i.subkey"));
- }
- };
- TWordCountHive elementStat = {{TString("Sort"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Sort"]);
- }
-
- Y_UNIT_TEST(SelectOrderByExpressionDesc) {
- NYql::TAstParseResult res = SqlToYql("pragma disablesimplecolumns; select i.*, key, subkey from plato.Input as i order by cast(i.key as uint32) - cast(i.subkey as uint32) desc");
- UNIT_ASSERT(res.Root);
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Sort") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"-MayWarn\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"subkey\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Bool 'false)"));
- } else if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'columns"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"subkey\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("prefix"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"i.\""));
- }
- };
- TWordCountHive elementStat = {{TString("Sort"), 0}, {TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Sort"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(SelectOrderByExpressionAsc) {
- NYql::TAstParseResult res = SqlToYql("select i.key, i.subkey from plato.Input as i order by cast(key as uint32) % cast(i.subkey as uint32) asc");
- UNIT_ASSERT(res.Root);
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Sort") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"%MayWarn\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"subkey\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Bool 'true)"));
- } else if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'columns"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"subkey\""));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("i."));
- }
- };
- TWordCountHive elementStat = {{TString("Sort"), 0}, {TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Sort"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(ReferenceToKeyInSubselect) {
- NYql::TAstParseResult res = SqlToYql("select b.key from (select a.key from plato.Input as a) as b;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(OrderByCastValue) {
- NYql::TAstParseResult res = SqlToYql("select i.key, i.subkey from plato.Input as i order by cast(key as uint32) desc;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(GroupByCastValue) {
- NYql::TAstParseResult res = SqlToYql("select count(1) from plato.Input as i group by cast(key as uint8);");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(KeywordInSelectColumns) {
- NYql::TAstParseResult res = SqlToYql("select in, s.check from (select 1 as in, \"test\" as check) as s;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectAllGroupBy) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input group by subkey;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(CreateObjectWithFeatures) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('\"K2\" '\"V2\") '('\"Key1\" '\"Value1\")"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(CreateObjectIfNotExists) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT IF NOT EXISTS secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObjectIfNotExists"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(CreateObjectWithFeaturesStrings) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET) WITH (Key1=\"Value1\", K2='V2', K3=V3, K4='', K5=`aaa`, K6='a\\'aa');");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('\"K2\" '\"V2\") '('\"K3\" '\"V3\") '('\"K4\" '\"\") '('\"K5\" '\"aaa\") '('\"K6\" '\"a'aa\") '('\"Key1\" '\"Value1\")"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}, {TString("SECRET"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- }
-
- Y_UNIT_TEST(UpsertObjectWithFeatures) {
- NYql::TAstParseResult res = SqlToYql("USE plato; UPSERT OBJECT secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('\"K2\" '\"V2\") '('\"Key1\" '\"Value1\")"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("upsertObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(CreateObjectWithFeaturesAndFlags) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2, RECURSE);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('\"Key1\" '\"Value1\") '('\"RECURSE\")"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(Select1Type) {
- NYql::TAstParseResult res = SqlToYql("SELECT 1 type;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectTableType) {
- NYql::TAstParseResult res = SqlToYql("USE plato; SELECT * from T type;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(CreateObjectNoFeatures) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(AlterObjectWithFeatures) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato;\n"
- "declare $path as String;\n"
- "ALTER OBJECT secretId (TYPE SECRET) SET (Key1=$path, K2=V2);"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"Key1\" (EvaluateAtom \"$path\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"K2\" '\"V2\""));
-
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alterObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(AlterObjectNoFeatures) {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER OBJECT secretId (TYPE SECRET);");
- UNIT_ASSERT(!res.Root);
- }
-
- Y_UNIT_TEST(DropObjectNoFeatures) {
- NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT secretId (TYPE SECRET);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(DropObjectWithFeatures) {
- NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT secretId (TYPE SECRET) WITH (A, B, C);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(DropObjectWithOneOption) {
- NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT secretId (TYPE SECRET) WITH OVERRIDE;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"OVERRIDE\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(DropObjectIfExists) {
- NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT IF EXISTS secretId (TYPE SECRET);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObjectIfExists"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(PrimaryKeyParseCorrect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE tableName (Key Uint32, Subkey Int64, Value String, PRIMARY KEY (Key, Subkey));");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"Key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"Subkey\""));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}, {TString("primarykey"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["primarykey"]);
- }
-
- Y_UNIT_TEST(AlterDatabaseAst) {
- NYql::TAstParseResult request = SqlToYql("USE plato; ALTER DATABASE `/Root/test` OWNER TO user1;");
- UNIT_ASSERT(request.IsOk());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- Y_UNUSED(word);
-
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(
- R"(let world (Write! world sink (Key '('databasePath (String '"/Root/test"))) (Void) '('('mode 'alterDatabase) '('owner '"user1"))))"
- ));
- };
-
- TWordCountHive elementStat({TString("\'mode \'alterDatabase")});
- VerifyProgram(request, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["\'mode \'alterDatabase"]);
- }
-
- Y_UNIT_TEST(CreateTableNonNullableYqlTypeAstCorrect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32 not null);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (DataType 'Int32) '('columnConstrains '('('not_null))) '())))))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableNullableYqlTypeAstCorrect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableNonNullablePgTypeAstCorrect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a pg_int4 not null);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (PgType '_int4) '('columnConstrains '('('not_null))) '())))))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableNullablePgTypeAstCorrect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a pg_int4);");
- UNIT_ASSERT(res.Root);
-
- res.Root->PrettyPrintTo(Cout, PRETTY_FLAGS);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (PgType '_int4)) '('columnConstrains '()) '()))))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableNullPkColumnsAreAllowed) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32, primary key(a));");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableNotNullPkColumnsAreIdempotentAstCorrect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32 not null, primary key(a));");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (DataType 'Int32) '('columnConstrains '('('not_null))) '()))) '('primarykey '('"a"))))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableWithIfNotExists) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE IF NOT EXISTS t (a int32, primary key(a));");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create_if_not_exists) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTempTable) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TEMP TABLE t (a int32, primary key(a));");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")) '('temporary))))__"), line);
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTemporaryTable) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TEMPORARY TABLE t (a int32, primary key(a));");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")) '('temporary))))__"), line);
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableWithoutTypes) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a, primary key(a));");
- UNIT_ASSERT(!res.Root);
- }
-
- Y_UNIT_TEST(CreateTableAsSelectWithTypes) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32, primary key(a)) AS SELECT * FROM ts;");
- UNIT_ASSERT(!res.Root);
- }
-
- Y_UNIT_TEST(CreateTableAsSelect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a, b, primary key(a)) AS SELECT * FROM ts;");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((let world (Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a") '('"b"))) '('primarykey '('"a"))))))__"));
- }
- if (word == "Read!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Read! world (DataSource '"yt" '"plato") (MrTableConcat (Key '('table (String '"ts")))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}, {TString("Read!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Read!"]);
- }
-
- Y_UNIT_TEST(CreateTableAsSelectOnlyPrimary) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (primary key(a)) AS SELECT * FROM ts;");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((let world (Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '()) '('primarykey '('"a"))))))__"));
- }
- if (word == "Read!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Read! world (DataSource '"yt" '"plato") (MrTableConcat (Key '('table (String '"ts")))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}, {TString("Read!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Read!"]);
- }
-
- Y_UNIT_TEST(CreateTableAsValuesFail) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a, primary key(a)) AS VALUES (1), (2);");
- UNIT_ASSERT(!res.Root);
- }
-
- Y_UNIT_TEST(CreateTableDuplicatedPkColumnsFail) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32 not null, primary key(a, a));");
- UNIT_ASSERT(!res.Root);
- }
-
- Y_UNIT_TEST(DeleteFromTableByKey) {
- NYql::TAstParseResult res = SqlToYql("delete from plato.Input where key = 200;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DeleteFromTable) {
- NYql::TAstParseResult res = SqlToYql("delete from plato.Input;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DeleteFromTableBatch) {
- NYql::TAstParseResult res = SqlToYql("batch delete from plato.Input;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete)"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('is_batch 'true)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DeleteFromTableOnValues) {
- NYql::TAstParseResult res = SqlToYql("delete from plato.Input on (key) values (1);",
- 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete_on)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DeleteFromTableOnSelect) {
- NYql::TAstParseResult res = SqlToYql(
- "delete from plato.Input on select key from plato.Input where value > 0;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete_on)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DeleteFromTableOnBatch) {
- NYql::TAstParseResult res = SqlToYql("batch delete from plato.Input on (key) values (1);",
- 10, "kikimr");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:6: Error: BATCH DELETE is unsupported with ON\n");
- }
-
- Y_UNIT_TEST(UpdateByValues) {
- NYql::TAstParseResult res = SqlToYql("update plato.Input set key = 777, value = 'cool' where key = 200;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)"));
- } else if (word == "AsStruct") {
- const bool isKey = line.find("key") != TString::npos;
- const bool isValue = line.find("value") != TString::npos;
- UNIT_ASSERT(isKey || isValue);
- if (isKey) {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("777")));
- } else if (isValue) {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("cool")));
- }
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}, {TString("AsStruct"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AsStruct"]);
- }
-
- Y_UNIT_TEST(UpdateByValuesBatch) {
- NYql::TAstParseResult res = SqlToYql("batch update plato.Input set key = 777, value = 'cool' where key = 200;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('is_batch 'true)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(UpdateByMultiValues) {
- NYql::TAstParseResult res = SqlToYql("update plato.Input set (key, value, subkey) = ('2','ddd',':') where key = 200;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)"));
- } else if (word == "AsStruct") {
- const bool isKey = line.find("key") != TString::npos;
- const bool isSubkey = line.find("subkey") != TString::npos;
- const bool isValue = line.find("value") != TString::npos;
- UNIT_ASSERT(isKey || isSubkey || isValue);
- if (isKey && !isSubkey) {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("2")));
- } else if (isSubkey) {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote(":")));
- } else if (isValue) {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("ddd")));
- }
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}, {TString("AsStruct"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AsStruct"]);
- }
-
- Y_UNIT_TEST(UpdateBySelect) {
- NYql::TAstParseResult res = SqlToYql("update plato.Input set (key, value, subkey) = (select key, value, subkey from plato.Input where key = 911) where key = 200;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- int lineIndex = 0;
- int writeLineIndex = -1;
- bool found = false;
-
- TVerifyLineFunc verifyLine = [&lineIndex, &writeLineIndex, &found](const TString& word, const TString& line) {
- if (word == "Write") {
- writeLineIndex = lineIndex;
- found = line.find("('mode 'update)") != TString::npos;
- } else if (word == "mode") {
- found |= lineIndex == writeLineIndex + 1 && line.find("('mode 'update)") != TString::npos;
- UNIT_ASSERT(found);
- }
-
- ++lineIndex;
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}, {TString("mode"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(UpdateSelfModifyAll) {
- NYql::TAstParseResult res = SqlToYql("update plato.Input set subkey = subkey + 's';", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)"));
- } else if (word == "AsStruct") {
- const bool isSubkey = line.find("subkey") != TString::npos;
- UNIT_ASSERT(isSubkey);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("subkey")));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("s")));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}, {TString("AsStruct"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AsStruct"]);
- }
-
- Y_UNIT_TEST(UpdateOnValues) {
- NYql::TAstParseResult res = SqlToYql("update plato.Input on (key, value) values (5, 'cool')", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update_on)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(UpdateOnSelect) {
- NYql::TAstParseResult res = SqlToYql(
- "update plato.Input on select key, value + 1 as value from plato.Input", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update_on)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(UpdateOnBatch) {
- NYql::TAstParseResult res = SqlToYql("batch update plato.Input on (key, value) values (5, 'cool')", 10, "kikimr");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:6: Error: BATCH UPDATE is unsupported with ON\n");
- }
-
- Y_UNIT_TEST(UnionAllTest) {
- NYql::TAstParseResult res = SqlToYql("PRAGMA DisableEmitUnionMerge; SELECT key FROM plato.Input UNION ALL select subkey FROM plato.Input;");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("UnionAll"), 0}};
- VerifyProgram(res, elementStat, {});
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["UnionAll"]);
- }
-
- Y_UNIT_TEST(UnionAllMergeTest) {
- NYql::TAstParseResult res = SqlToYql("PRAGMA EmitUnionMerge; SELECT key FROM plato.Input UNION ALL select subkey FROM plato.Input;");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("UnionMerge"), 0}};
- VerifyProgram(res, elementStat, {});
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["UnionMerge"]);
- }
-
- Y_UNIT_TEST(UnionTest) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input UNION select subkey FROM plato.Input;");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("Union"), 0}};
- VerifyProgram(res, elementStat, {});
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Union"]);
- }
-
- Y_UNIT_TEST(UnionAggregationTest) {
- NYql::TAstParseResult res = SqlToYql(R"(
- PRAGMA DisableEmitUnionMerge;
- SELECT 1
- UNION ALL
- SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
- UNION
- SELECT 1 UNION SELECT 1 UNION SELECT 1 UNION SELECT 1
- UNION ALL
- SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1;
- )");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("Union"), 0}, {TString("UnionAll"), 0}};
- VerifyProgram(res, elementStat, {});
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["UnionAll"]);
- UNIT_ASSERT_VALUES_EQUAL(3, elementStat["Union"]);
- }
-
- Y_UNIT_TEST(UnionMergeAggregationTest) {
- NYql::TAstParseResult res = SqlToYql(R"(
- PRAGMA EmitUnionMerge;
- SELECT 1
- UNION ALL
- SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
- UNION
- SELECT 1 UNION SELECT 1 UNION SELECT 1 UNION SELECT 1
- UNION ALL
- SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1;
- )");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("Union"), 0}, {TString("UnionMerge"), 0}};
- VerifyProgram(res, elementStat, {});
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["UnionMerge"]);
- UNIT_ASSERT_VALUES_EQUAL(3, elementStat["Union"]);
- }
-
- Y_UNIT_TEST(DeclareDecimalParameter) {
- NYql::TAstParseResult res = SqlToYql("declare $value as Decimal(22,9); select $value as cnt;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SimpleGroupBy) {
- NYql::TAstParseResult res = SqlToYql("select count(1),z from plato.Input group by key as z order by z;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(EmptyColumnName0) {
- /// Now it's parsed well and error occur on validate step like "4:31:Empty struct member name is not allowed" in "4:31:Function: AddMember"
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output (``, list1) values (0, AsList(0, 1, 2));");
- /// Verify that parsed well without crash
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(KikimrRollback) {
- NYql::TAstParseResult res = SqlToYql("use plato; select * from Input; rollback;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("rollback"), 0}};
- VerifyProgram(res, elementStat);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["rollback"]);
- }
-
- Y_UNIT_TEST(PragmaFile) {
- NYql::TAstParseResult res = SqlToYql(R"(pragma file("HW", "sbr:181041334");)");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString(R"((let world (Configure! world (DataSource '"config") '"AddFileByUrl" '"HW" '"sbr:181041334")))"), 0}};
- VerifyProgram(res, elementStat);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat.cbegin()->second);
- }
-
- Y_UNIT_TEST(DoNotCrashOnNamedInFilter) {
- NYql::TAstParseResult res = SqlToYql("USE plato; $all = ($table_name) -> { return true; }; SELECT * FROM FILTER(Input, $all)");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(PragmasFileAndUdfOrder) {
- NYql::TAstParseResult res = SqlToYql(R"(
- PRAGMA file("libvideoplayers_udf.so", "https://proxy.sandbox.yandex-team.ru/235185290");
- PRAGMA udf("libvideoplayers_udf.so");
- )");
- UNIT_ASSERT(res.Root);
-
- const auto programm = GetPrettyPrint(res);
- const auto file = programm.find("AddFileByUrl");
- const auto udfs = programm.find("ImportUdfs");
- UNIT_ASSERT(file < udfs);
- }
-
- Y_UNIT_TEST(ProcessUserType) {
- NYql::TAstParseResult res = SqlToYql("process plato.Input using Kikimr::PushData(TableRows());", 1, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Kikimr.PushData") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("TupleType"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("TypeOf"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Kikimr.PushData"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Kikimr.PushData"]);
- }
-
- Y_UNIT_TEST(ProcessUserTypeAuth) {
- NYql::TAstParseResult res = SqlToYql("process plato.Input using YDB::PushData(TableRows(), AsTuple('oauth', SecureParam('api:oauth')));", 1, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "YDB.PushData") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("TupleType"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("TypeOf"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("api:oauth"));
- }
- };
-
- TWordCountHive elementStat = {{TString("YDB.PushData"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["YDB.PushData"]);
- }
-
- Y_UNIT_TEST(SelectStreamRtmr) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato; INSERT INTO Output SELECT STREAM key FROM Input;",
- 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(res.Root);
-
- res = SqlToYql(
- "USE plato; INSERT INTO Output SELECT key FROM Input;",
- 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectStreamRtmrJoinWithYt) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato; INSERT INTO Output SELECT STREAM key FROM Input LEFT JOIN hahn.ttt as t ON Input.key = t.Name;",
- 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectStreamNonRtmr) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato; INSERT INTO Output SELECT STREAM key FROM Input;",
- 10);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:31: Error: SELECT STREAM is unsupported for non-streaming sources\n");
- }
-
- Y_UNIT_TEST(GroupByHopRtmr) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato; INSERT INTO Output SELECT key, SUM(value) AS value FROM Input
- GROUP BY key, HOP(subkey, "PT10S", "PT30S", "PT20S");
- )", 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(GroupByHopRtmrSubquery) {
- // 'use plato' intentially avoided
- NYql::TAstParseResult res = SqlToYql(R"(
- SELECT COUNT(*) AS value FROM (SELECT * FROM plato.Input)
- GROUP BY HOP(Data, "PT10S", "PT30S", "PT20S")
- )", 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(GroupByHopRtmrSubqueryBinding) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- $q = SELECT * FROM Input;
- INSERT INTO Output SELECT STREAM * FROM (
- SELECT COUNT(*) AS value FROM $q
- GROUP BY HOP(Data, "PT10S", "PT30S", "PT20S")
- );
- )", 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(GroupByNoHopRtmr) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato; INSERT INTO Output SELECT STREAM key, SUM(value) AS value FROM Input
- GROUP BY key;
- )", 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:22: Error: Streaming group by query must have a hopping window specification.\n");
- }
-
- Y_UNIT_TEST(KikimrInserts) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- INSERT INTO Output SELECT key, value FROM Input;
- INSERT OR ABORT INTO Output SELECT key, value FROM Input;
- INSERT OR IGNORE INTO Output SELECT key, value FROM Input;
- INSERT OR REVERT INTO Output SELECT key, value FROM Input;
- )", 10, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(WarnMissingIsBeforeNotNull) {
- NYql::TAstParseResult res = SqlToYql("select 1 NOT NULL");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Warning: Missing IS keyword before NOT NULL, code: 4507\n");
- }
-
- Y_UNIT_TEST(Subqueries) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- $sq1 = (SELECT * FROM plato.Input);
-
- $sq2 = SELECT * FROM plato.Input;
-
- $squ1 = (
- SELECT * FROM plato.Input
- UNION ALL
- SELECT * FROM plato.Input
- );
-
- $squ2 =
- SELECT * FROM plato.Input
- UNION ALL
- SELECT * FROM plato.Input;
-
- $squ3 = (
- (SELECT * FROM plato.Input)
- UNION ALL
- (SELECT * FROM plato.Input)
- );
-
- SELECT * FROM $sq1;
- SELECT * FROM $sq2;
- SELECT * FROM $squ1;
- SELECT * FROM $squ2;
- SELECT * FROM $squ3;
- )");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SubqueriesJoin) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
-
- $left = SELECT * FROM plato.Input1 WHERE value != "BadValue";
- $right = SELECT * FROM plato.Input2;
-
- SELECT * FROM $left AS l
- JOIN $right AS r
- ON l.key == r.key;
- )");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(AnyInBackticksAsTableName) {
- NYql::TAstParseResult res = SqlToYql("use plato; select * from `any`;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(AnyJoinForTableAndSubQuery) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
-
- $r = SELECT * FROM plato.Input2;
-
- SELECT * FROM ANY plato.Input1 AS l
- LEFT JOIN ANY $r AS r
- USING (key);
- )");
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "EquiJoin") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('left 'any)"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('right 'any)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("left"), 0}, {TString("right"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["left"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["right"]);
- }
-
- Y_UNIT_TEST(AnyJoinForTableAndTableSource) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
-
- $r = AsList(
- AsStruct("aaa" as key, "bbb" as subkey, "ccc" as value)
- );
-
- SELECT * FROM ANY plato.Input1 AS l
- LEFT JOIN ANY AS_TABLE($r) AS r
- USING (key);
- )");
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "EquiJoin") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('left 'any)"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('right 'any)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("left"), 0}, {TString("right"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["left"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["right"]);
- }
-
- Y_UNIT_TEST(AnyJoinNested) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
-
- FROM ANY Input1 as a
- JOIN Input2 as b ON a.key = b.key
- LEFT JOIN ANY Input3 as c ON a.key = c.key
- RIGHT JOIN ANY Input4 as d ON d.key = b.key
- CROSS JOIN Input5
- SELECT *;
- )");
-
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("left"), 0}, {TString("right"), 0}};
- VerifyProgram(res, elementStat);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["left"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["right"]);
- }
-
- Y_UNIT_TEST(InlineAction) {
- NYql::TAstParseResult res = SqlToYql(
- "do begin\n"
- " select 1\n"
- "; end do\n");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "");
- }
-
- Y_UNIT_TEST(FlattenByCorrelationName) {
- UNIT_ASSERT(SqlToYql("select * from plato.Input as t flatten by t.x").IsOk());
- UNIT_ASSERT(SqlToYql("select * from plato.Input as t flatten by t -- same as flatten by t.t").IsOk());
- }
-
- Y_UNIT_TEST(DiscoveryMode) {
- UNIT_ASSERT(SqlToYqlWithMode("insert into plato.Output select * from plato.Input", NSQLTranslation::ESqlMode::DISCOVERY).IsOk());
- UNIT_ASSERT(SqlToYqlWithMode("select * from plato.concat(Input1, Input2)", NSQLTranslation::ESqlMode::DISCOVERY).IsOk());
- UNIT_ASSERT(SqlToYqlWithMode("select * from plato.each(AsList(\"Input1\", \"Input2\"))", NSQLTranslation::ESqlMode::DISCOVERY).IsOk());
- }
-
- Y_UNIT_TEST(CubeWithAutoGeneratedLikeColumnName) {
- UNIT_ASSERT(SqlToYql("select key,subkey,group from plato.Input group by cube(key,subkey,group)").IsOk());
- }
-
- Y_UNIT_TEST(CubeWithAutoGeneratedLikeAlias) {
- UNIT_ASSERT(SqlToYql("select key,subkey,group from plato.Input group by cube(key,subkey,value as group)").IsOk());
- }
-
- Y_UNIT_TEST(FilterCanBeUsedAsColumnIdOrBind) {
- UNIT_ASSERT(SqlToYql("select filter from plato.Input").IsOk());
- UNIT_ASSERT(SqlToYql("select 1 as filter").IsOk());
- UNIT_ASSERT(SqlToYql("$filter = 1; select $filter").IsOk());
- }
-
- Y_UNIT_TEST(DuplicateSemicolonsAreAllowedBetweenTopLevelStatements) {
- UNIT_ASSERT(SqlToYql(";;select 1; ; select 2;/*comment*/;select 3;;--comment\n;select 4;;").IsOk());
- }
-
- Y_UNIT_TEST(DuplicateAndMissingTrailingSemicolonsAreAllowedBetweenActionStatements) {
- TString req =
- "define action $action($b,$c) as\n"
- " ;;$d = $b + $c;\n"
- " select $b;\n"
- " select $c;;\n"
- " select $d,\n"
- "end define;\n"
- "\n"
- "do $action(1,2);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(DuplicateAndMissingTrailingSemicolonsAreAllowedBetweenInlineActionStatements) {
- TString req =
- "do begin\n"
- " ;select 1,\n"
- "end do;\n"
- "evaluate for $i in AsList(1,2,3) do begin\n"
- " select $i;;\n"
- " select $i + $i;;\n"
- "end do;";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(DuplicateSemicolonsAreAllowedBetweenLambdaStatements) {
- TString req =
- "$x=1;\n"
- "$foo = ($a, $b)->{\n"
- " ;;$v = $a + $b;\n"
- " $bar = ($c) -> {; return $c << $x};;\n"
- " return $bar($v);;\n"
- "};\n"
- "select $foo(1,2);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(StringLiteralWithEscapedBackslash) {
- NYql::TAstParseResult res1 = SqlToYql(R"foo(SELECT 'a\\';)foo");
- NYql::TAstParseResult res2 = SqlToYql(R"foo(SELECT "a\\";)foo");
- UNIT_ASSERT(res1.Root);
- UNIT_ASSERT(res2.Root);
-
- TWordCountHive elementStat = {{TString("a\\"), 0}};
-
- VerifyProgram(res1, elementStat);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["a\\"]);
-
- VerifyProgram(res2, elementStat);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["a\\"]);
- }
-
- Y_UNIT_TEST(StringMultiLineLiteralWithEscapes) {
- UNIT_ASSERT(SqlToYql("SELECT @@@foo@@@@bar@@@").IsOk());
- UNIT_ASSERT(SqlToYql("SELECT @@@@@@@@@").IsOk());
- }
-
- Y_UNIT_TEST(StringMultiLineLiteralConsequitiveAt) {
- UNIT_ASSERT(!SqlToYql("SELECT @").IsOk());
- UNIT_ASSERT(!SqlToYql("SELECT @@").IsOk());
- UNIT_ASSERT(!SqlToYql("SELECT @@@").IsOk());
- UNIT_ASSERT( SqlToYql("SELECT @@@@").IsOk());
- UNIT_ASSERT( SqlToYql("SELECT @@@@@").IsOk());
-
- UNIT_ASSERT(!SqlToYql("SELECT @@@@@@").IsOk());
- UNIT_ASSERT(!SqlToYql("SELECT @@@@@@@").IsOk());
-
- UNIT_ASSERT( SqlToYql("SELECT @@@@@@@@").IsOk());
- UNIT_ASSERT( SqlToYql("SELECT @@@@@@@@@").IsOk());
- UNIT_ASSERT(!SqlToYql("SELECT @@@@@@@@@@").IsOk());
- }
-
- Y_UNIT_TEST(ConstnessForListDictSetCreate) {
- auto req = "$foo = ($x, $y) -> (\"aaaa\");\n"
- "\n"
- "select\n"
- " $foo(sum(key), ListCreate(String)),\n"
- " $foo(sum(key), DictCreate(String, String)),\n"
- " $foo(sum(key), SetCreate(String)),\n"
- "from (select 1 as key);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(CanUseEmptyTupleInWindowPartitionBy) {
- auto req = "select sum(key) over w\n"
- "from plato.Input\n"
- "window w as (partition compact by (), (subkey), (), value || value as dvalue);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(DenyAnsiOrderByLimitLegacyMode) {
- auto req = "pragma DisableAnsiOrderByLimitInUnionAll;\n"
- "use plato;\n"
- "\n"
- "select * from Input order by key limit 10\n"
- "union all\n"
- "select * from Input order by key limit 1;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: DisableAnsiOrderByLimitInUnionAll pragma is deprecated and no longer supported\n");
- }
-
- Y_UNIT_TEST(ReduceUsingUdfWithShortcutsWorks) {
- auto req = "use plato;\n"
- "\n"
- "$arg = 'foo';\n"
- "$func = XXX::YYY($arg);\n"
- "\n"
- "REDUCE Input ON key using $func(subkey);\n"
- "REDUCE Input ON key using $func(UUU::VVV(TableRow()));\n";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- req = "use plato;\n"
- "\n"
- "$arg = 'foo';\n"
- "$func = XXX::YYY($arg);\n"
- "\n"
- "REDUCE Input ON key using all $func(subkey);\n"
- "REDUCE Input ON key using all $func(UUU::VVV(TableRow()));";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(YsonDisableStrict) {
- UNIT_ASSERT(SqlToYql("pragma yson.DisableStrict = \"false\";").IsOk());
- UNIT_ASSERT(SqlToYql("pragma yson.DisableStrict;").IsOk());
- }
-
- Y_UNIT_TEST(YsonStrict) {
- UNIT_ASSERT(SqlToYql("pragma yson.Strict = \"false\";").IsOk());
- UNIT_ASSERT(SqlToYql("pragma yson.Strict;").IsOk());
- }
-
- Y_UNIT_TEST(JoinByTuple) {
- auto req = "use plato;\n"
- "\n"
- "select * from T1 as a\n"
- "join T2 as b\n"
- "on AsTuple(a.key, a.subkey) = AsTuple(b.key, b.subkey);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(JoinByStruct) {
- auto req = "use plato;\n"
- "\n"
- "select * from T1 as a\n"
- "join T2 as b\n"
- "on AsStruct(a.key as k, a.subkey as sk) = AsStruct(b.key as k, b.subkey as sk);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(JoinByUdf) {
- auto req = "use plato;\n"
- "\n"
- "select a.align\n"
- "from T1 as a\n"
- "join T2 as b\n"
- "on Yson::SerializeJsonEncodeUtf8(a.align)=b.align;";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(EscapedIdentifierAsLambdaArg) {
- auto req = "$f = ($`foo bar`, $x) -> { return $`foo bar` + $x; };\n"
- "\n"
- "select $f(1, 2);";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(lambda '(\"$foo bar\" \"$x\")";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarOnlyCallable) {
- auto req = "SELECT Udf(DateTime::FromString)('2022-01-01');";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType)))";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarTypeNoRun) {
- auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, 'foo' as TypeConfig)('2022-01-01');";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\")";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarRunNoType) {
- auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, Void() as RunConfig)('2022-01-01');";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"\" (Void))";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarFullTest) {
- auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, 'foo' as TypeConfig, Void() As RunConfig)('2022-01-01');";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\" (Void))";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarOtherRunConfigs) {
- auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, 'foo' as TypeConfig, '55' As RunConfig)('2022-01-01');";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\" (String '\"55\"))";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarOtherRunConfigs2) {
- auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, 'foo' as TypeConfig, AsTuple(32, 'no', AsStruct(1e-9 As SomeFloat)) As RunConfig)('2022-01-01');";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\" '((Int32 '\"32\") (String '\"no\") (AsStruct '('\"SomeFloat\" (Double '\"1e-9\")))))";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarOptional) {
- auto req = "SELECT Udf(DateTime::FromString, String?, Int32??, Tuple<Int32, Float>, \"foo\" as TypeConfig, Void() As RunConfig)(\"2022-01-01\");";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (OptionalType (DataType 'String)) (OptionalType (OptionalType (DataType 'Int32))) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\" (Void))";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(CompactionPolicyParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
- WITH ( COMPACTION_POLICY = "SomeCompactionPreset" );)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("compactionPolicy"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("SomeCompactionPreset"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AutoPartitioningBySizeParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
- WITH ( AUTO_PARTITIONING_BY_SIZE = ENABLED );)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("autoPartitioningBySize"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("ENABLED"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(UniformPartitionsParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
- WITH ( UNIFORM_PARTITIONS = 16 );)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("uniformPartitions"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("16"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DateTimeTtlParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, CreatedAt Timestamp, PRIMARY KEY (Key))
- WITH (TTL = Interval("P1D") On CreatedAt);)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("setTtlSettings"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiers"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("evictionDelay"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("86400000"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(IntTtlParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, CreatedAt Uint32, PRIMARY KEY (Key))
- WITH (TTL = Interval("P1D") On CreatedAt AS SECONDS);)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("setTtlSettings"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiers"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("evictionDelay"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("86400000"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("columnUnit"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("seconds"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(TtlTieringParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, CreatedAt Uint32, PRIMARY KEY (Key))
- WITH (TTL =
- Interval("P1D") TO EXTERNAL DATA SOURCE Tier1,
- Interval("P2D") TO EXTERNAL DATA SOURCE Tier2,
- Interval("P30D") DELETE
- ON CreatedAt AS SECONDS);)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("setTtlSettings"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiers"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("evictionDelay"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("storageName"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Tier1"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Tier2"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("86400000"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("172800000"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("2592000000"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("columnUnit"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("seconds"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(TtlTieringWithOtherActionsParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- ALTER TABLE tableName
- ADD FAMILY cold (DATA = "rot"),
- SET TTL
- Interval("P1D") TO EXTERNAL DATA SOURCE Tier1,
- Interval("P2D") TO EXTERNAL DATA SOURCE Tier2,
- Interval("P30D") DELETE
- ON CreatedAt,
- ALTER COLUMN payload_v2 SET FAMILY cold,
- ALTER FAMILY default SET DATA "ssd"
- ;)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("addColumnFamilies"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("cold"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alterColumnFamilies"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("default"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("setTtlSettings"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiers"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("evictionDelay"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("storageName"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Tier1"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Tier2"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("86400000"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("172800000"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("2592000000"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(TieringParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
- WITH ( TIERING = 'my_tiering' );)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiering"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("my_tiering"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(StoreExternalBlobsParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
- WITH ( STORE_EXTERNAL_BLOBS = ENABLED );)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("storeExternalBlobs"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("ENABLED"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DefaultValueColumn2) {
- auto res = SqlToYql(R"( use plato;
- $lambda = () -> {
- RETURN CAST(RandomUuid(2) as String)
- };
-
- CREATE TABLE tableName (
- Key Uint32 DEFAULT RandomNumber(1),
- Value String DEFAULT $lambda,
- PRIMARY KEY (Key)
- );
- )");
-
- UNIT_ASSERT_C(res.Root, Err2Str(res));
-
- const auto program = GetPrettyPrint(res);
-
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("RandomNumber"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("RandomUuid"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("columnConstrains"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("columnConstrains"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("Write"));
-
-#if 0
- Cerr << program << Endl;
-#endif
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DefaultValueColumn3) {
- auto res = SqlToYql(R"( use plato;
-
- CREATE TABLE tableName (
- database_id Utf8,
- cloud_id Utf8,
- global_id Utf8 DEFAULT database_id || "=====",
- PRIMARY KEY (database_id)
- );
- )");
-
- UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:6:40: Error: Column reference \"database_id\" is not allowed in current scope\n");
- UNIT_ASSERT(!res.Root);
- }
-
- Y_UNIT_TEST(DefaultValueColumn) {
- auto res = SqlToYql(R"( use plato;
- CREATE TABLE tableName (
- Key Uint32 FAMILY cold DEFAULT 5,
- Value String FAMILY default DEFAULT "empty",
- PRIMARY KEY (Key),
- FAMILY default (
- DATA = "test",
- COMPRESSION = "lz4"
- ),
- FAMILY cold (
- DATA = "test",
- COMPRESSION = "off"
- )
- );
- )");
-
- UNIT_ASSERT_C(res.Root, Err2Str(res));
-
-#if 0
- const auto program = GetPrettyPrint(res);
- Cerr << program << Endl;
-#endif
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("default"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("columnConstrains"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("columnFamilies"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(ChangefeedParseCorrect) {
- auto res = SqlToYql(R"( USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (
- MODE = 'KEYS_ONLY',
- FORMAT = 'json',
- INITIAL_SCAN = TRUE,
- VIRTUAL_TIMESTAMPS = FALSE,
- BARRIERS_INTERVAL = Interval("PT1S"),
- RETENTION_PERIOD = Interval("P1D"),
- TOPIC_MIN_ACTIVE_PARTITIONS = 10,
- AWS_REGION = 'aws:region'
- )
- );
- )");
- UNIT_ASSERT_C(res.Root, Err2Str(res));
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("changefeed"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("mode"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("KEYS_ONLY"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("format"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("json"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("initial_scan"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("true"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("virtual_timestamps"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("false"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("barriers_interval"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("retention_period"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("topic_min_active_partitions"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("aws_region"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("aws:region"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CloneForAsTableWorksWithCube) {
- UNIT_ASSERT(SqlToYql("SELECT * FROM AS_TABLE([<|k1:1, k2:1|>]) GROUP BY CUBE(k1, k2);").IsOk());
- }
-
- Y_UNIT_TEST(WindowPartitionByColumnProperlyEscaped) {
- NYql::TAstParseResult res = SqlToYql("SELECT SUM(key) OVER w FROM plato.Input WINDOW w AS (PARTITION BY `column with space`);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "CalcOverWindow") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"column with space\""));
- }
- };
-
- TWordCountHive elementStat = { {TString("CalcOverWindow"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["CalcOverWindow"]);
- }
-
- Y_UNIT_TEST(WindowPartitionByExpressionWithoutAliasesAreAllowed) {
- NYql::TAstParseResult res = SqlToYql("SELECT SUM(key) OVER w FROM plato.Input as i WINDOW w AS (PARTITION BY ii.subkey);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "AddMember") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("AddMember row 'group_w_0 (SqlAccess 'struct (Member row '\"ii\")"));
- }
- if (word == "CalcOverWindow") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("CalcOverWindow core '('\"group_w_0\")"));
- }
- };
-
- TWordCountHive elementStat = { {TString("CalcOverWindow"), 0}, {TString("AddMember"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["CalcOverWindow"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AddMember"]);
- }
-
- Y_UNIT_TEST(PqReadByAfterUse) {
- ExpectFailWithError("use plato; pragma PqReadBy='plato2';",
- "<main>:1:28: Error: Cluster in PqReadPqBy pragma differs from cluster specified in USE statement: plato2 != plato\n");
-
- UNIT_ASSERT(SqlToYql("pragma PqReadBy='plato2';").IsOk());
- UNIT_ASSERT(SqlToYql("pragma PqReadBy='plato2'; use plato;").IsOk());
- UNIT_ASSERT(SqlToYql("$x='plato'; use rtmr:$x; pragma PqReadBy='plato2';").IsOk());
- UNIT_ASSERT(SqlToYql("use plato; pragma PqReadBy='dq';").IsOk());
- }
-
- Y_UNIT_TEST(MrObject) {
- NYql::TAstParseResult res = SqlToYql(
- "declare $path as String;\n"
- "select * from plato.object($path, `format`, \"comp\" || \"ression\" as compression, 1 as bar) with schema (Int32 as y, String as x)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "MrObject") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((MrObject (EvaluateAtom "$path") '"format" '('('"compression" (Concat (String '"comp") (String '"ression"))) '('"bar" (Int32 '"1")))))__"));
- } else if (word == "userschema") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__('('('"userschema" (StructType '('"y" (DataType 'Int32)) '('"x" (DataType 'String))) '('"y" '"x"))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("MrObject"), 0}, {TString("userschema"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["MrObject"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["userschema"]);
- }
-
- Y_UNIT_TEST(TableBindings) {
- NSQLTranslation::TTranslationSettings settings = GetSettingsWithS3Binding("foo");
- NYql::TAstParseResult res = SqlToYqlWithSettings(
- "select * from bindings.foo",
- settings
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "MrObject") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((MrTableConcat (Key '('table (String '"path")))) (Void) '('('"bar" '"1") '('"compression" '"ccompression") '('"format" '"format") '('"partitionedby" '"key" '"subkey") '('"userschema" (SqlTypeFromYson)__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("MrTableConcat"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["MrTableConcat"]);
-
- settings.DefaultCluster = "plato";
- settings.BindingsMode = NSQLTranslation::EBindingsMode::DISABLED;
- res = SqlToYqlWithSettings(
- "select * from bindings.foo",
- settings
- );
- UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:1:15: Error: Please remove 'bindings.' from your query, the support for this syntax has ended, code: 4601\n");
- UNIT_ASSERT(!res.Root);
-
- settings.BindingsMode = NSQLTranslation::EBindingsMode::DROP;
- res = SqlToYqlWithSettings(
- "select * from bindings.foo",
- settings
- );
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine2 = [](const TString& word, const TString& line) {
- if (word == "MrTableConcat") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((MrTableConcat (Key '('table (String '"foo")))) (Void) '())))__"));
- }
- };
-
- TWordCountHive elementStat2 = {{TString("MrTableConcat"), 0}};
- VerifyProgram(res, elementStat2, verifyLine2);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat2["MrTableConcat"]);
-
- settings.BindingsMode = NSQLTranslation::EBindingsMode::DROP_WITH_WARNING;
- res = SqlToYqlWithSettings(
- "select * from bindings.foo",
- settings
- );
- UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:1:15: Warning: Please remove 'bindings.' from your query, the support for this syntax will be dropped soon, code: 4538\n");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat3 = {{TString("MrTableConcat"), 0}};
- VerifyProgram(res, elementStat3, verifyLine2);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat3["MrTableConcat"]);
- }
-
- Y_UNIT_TEST(TableBindingsWithInsert) {
- NSQLTranslation::TTranslationSettings settings = GetSettingsWithS3Binding("foo");
- NYql::TAstParseResult res = SqlToYqlWithSettings(
- "insert into bindings.foo with truncate (x, y) values (1, 2);",
- settings
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('table (String '"path"))) values '('('"bar" '"1") '('"compression" '"ccompression") '('"format" '"format") '('"partitionedby" '"key" '"subkey") '('"userschema" (SqlTypeFromYson)__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
-
- settings.DefaultCluster = "plato";
- settings.BindingsMode = NSQLTranslation::EBindingsMode::DISABLED;
- res = SqlToYqlWithSettings(
- "insert into bindings.foo with truncate (x, y) values (1, 2);",
- settings
- );
- UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:1:13: Error: Please remove 'bindings.' from your query, the support for this syntax has ended, code: 4601\n");
- UNIT_ASSERT(!res.Root);
-
- settings.BindingsMode = NSQLTranslation::EBindingsMode::DROP;
- res = SqlToYqlWithSettings(
- "insert into bindings.foo with truncate (x, y) values (1, 2);",
- settings
- );
- UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine2 = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- //UNIT_ASSERT_VALUES_EQUAL(line, "");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('table (String '"foo"))) values '('('mode 'renew)))__"));
- }
- };
-
- TWordCountHive elementStat2 = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat2, verifyLine2);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat2["Write!"]);
-
- settings.BindingsMode = NSQLTranslation::EBindingsMode::DROP_WITH_WARNING;
- res = SqlToYqlWithSettings(
- "insert into bindings.foo with truncate (x, y) values (1, 2);",
- settings
- );
- UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:1:13: Warning: Please remove 'bindings.' from your query, the support for this syntax will be dropped soon, code: 4538\n");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat3 = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat3, verifyLine2);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat3["Write!"]);
- }
-
- Y_UNIT_TEST(TrailingCommaInWithout) {
- UNIT_ASSERT(SqlToYql("SELECT * WITHOUT stream, FROM plato.Input").IsOk());
- UNIT_ASSERT(SqlToYql("SELECT a.* WITHOUT a.intersect, FROM plato.Input AS a").IsOk());
- UNIT_ASSERT(SqlToYql("SELECT a.* WITHOUT col1, col2, a.col3, FROM plato.Input AS a").IsOk());
- }
-
- Y_UNIT_TEST(NoStackOverflowOnBigCaseStatement) {
- TStringBuilder req;
- req << "select case 1 + 123";
- for (size_t i = 0; i < 20000; ++i) {
- req << " when " << i << " then " << i + 1;
- }
- req << " else 100500 end;";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(CollectPreaggregatedInListLiteral) {
- UNIT_ASSERT(SqlToYql("SELECT [COUNT(DISTINCT a+b)] FROM plato.Input").IsOk());
- }
-
- Y_UNIT_TEST(SmartParenInGroupByClause) {
- UNIT_ASSERT(SqlToYql("SELECT * FROM plato.Input GROUP BY (k, v)").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableRenameToIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table RENAME TO moved").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableAddDropColumnIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table ADD COLUMN addc uint64, DROP COLUMN dropc, ADD addagain uint64").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableSetTTLIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table SET (TTL = Interval(\"PT3H\") ON column)").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table SET (TTL = Interval(\"PT3H\") ON column AS SECONDS)").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableSetTieringIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table SET (TIERING = 'my_tiering')").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableAddChangefeedIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table ADD CHANGEFEED feed WITH (MODE = 'UPDATES', FORMAT = 'json')").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableAlterChangefeedIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table ALTER CHANGEFEED feed DISABLE").IsOk());
- ExpectFailWithError("USE plato; ALTER TABLE table ALTER CHANGEFEED feed SET (FORMAT = 'proto');",
- "<main>:1:57: Error: FORMAT alter is not supported\n");
- }
-
- Y_UNIT_TEST(AlterTableDropChangefeedIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table DROP CHANGEFEED feed").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableSetPartitioningIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table SET (AUTO_PARTITIONING_BY_SIZE = DISABLED)").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableAddIndexWithIsNotSupported) {
- ExpectFailWithFuzzyError("USE plato; ALTER TABLE table ADD INDEX idx GLOBAL ON (col) WITH (a=b)",
- "<main>:1:40: Error: with: alternative is not implemented yet: \\d+:\\d+: global_index\\n");
- }
-
- Y_UNIT_TEST(AlterTableAddIndexLocalIsNotSupported) {
- ExpectFailWithFuzzyError("USE plato; ALTER TABLE table ADD INDEX idx LOCAL ON (col)",
- "<main>:1:40: Error: local: alternative is not implemented yet: \\d+:\\d+: local_index\\n");
- }
-
- Y_UNIT_TEST(CreateTableAddIndexVector) {
- const auto result = SqlToYql(R"(USE plato;
- CREATE TABLE table (
- pk INT32 NOT NULL,
- col String,
- INDEX idx GLOBAL USING vector_kmeans_tree
- ON (col) COVER (col)
- WITH (distance=cosine, vector_type=float, vector_dimension=1024,),
- PRIMARY KEY (pk))
- )");
- UNIT_ASSERT_C(result.IsOk(), result.Issues.ToString());
- }
-
- Y_UNIT_TEST(AlterTableAddIndexVector) {
- const auto result = SqlToYql(R"(USE plato;
- ALTER TABLE table ADD INDEX idx
- GLOBAL USING vector_kmeans_tree
- ON (col) COVER (col)
- WITH (distance=cosine, vector_type="float", vector_dimension=1024)
- )");
- UNIT_ASSERT_C(result.IsOk(), result.Issues.ToString());
- }
-
- Y_UNIT_TEST(AlterTableAddIndexUnknownSubtype) {
- ExpectFailWithError("USE plato; ALTER TABLE table ADD INDEX idx GLOBAL USING unknown ON (col)",
- "<main>:1:57: Error: UNKNOWN index subtype is not supported\n");
- }
-
- Y_UNIT_TEST(AlterTableAddIndexMissedParameter) {
- ExpectFailWithError(R"(USE plato;
- ALTER TABLE table ADD INDEX idx
- GLOBAL USING vector_kmeans_tree
- ON (col)
- WITH (distance=cosine, vector_type=float)
- )",
- "<main>:5:52: Error: vector_dimension should be set\n");
- }
-
- Y_UNIT_TEST(AlterTableAlterIndexSetPartitioningIsCorrect) {
- const auto result = SqlToYql("USE plato; ALTER TABLE table ALTER INDEX index SET AUTO_PARTITIONING_MIN_PARTITIONS_COUNT 10");
- UNIT_ASSERT_C(result.IsOk(), result.Issues.ToString());
- }
-
- Y_UNIT_TEST(AlterTableAlterIndexSetMultiplePartitioningSettings) {
- const auto result = SqlToYql("USE plato; ALTER TABLE table ALTER INDEX index SET "
- "(AUTO_PARTITIONING_BY_LOAD = ENABLED, AUTO_PARTITIONING_MIN_PARTITIONS_COUNT = 10)"
- );
- UNIT_ASSERT_C(result.IsOk(), result.Issues.ToString());
- }
-
- Y_UNIT_TEST(AlterTableAlterIndexResetPartitioningIsNotSupported) {
- ExpectFailWithError("USE plato; ALTER TABLE table ALTER INDEX index RESET (AUTO_PARTITIONING_MIN_PARTITIONS_COUNT)",
- "<main>:1:55: Error: AUTO_PARTITIONING_MIN_PARTITIONS_COUNT reset is not supported\n"
- );
- }
-
- Y_UNIT_TEST(AlterTableAlterColumnDropNotNullAstCorrect) {
- auto reqSetNull = SqlToYql(R"(
- USE plato;
- CREATE TABLE tableName (
- id Uint32,
- val Uint32 NOT NULL,
- PRIMARY KEY (id)
- );
-
- COMMIT;
- ALTER TABLE tableName ALTER COLUMN val DROP NOT NULL;
- )");
-
- UNIT_ASSERT(reqSetNull.IsOk());
- UNIT_ASSERT(reqSetNull.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- Y_UNUSED(word);
-
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(
- R"(let world (Write! world sink (Key '('tablescheme (String '"tableName"))) (Void) '('('mode 'alter) '('actions '('('alterColumns '('('"val" '('changeColumnConstraints '('('drop_not_null)))))))))))"
- ));
- };
-
- TWordCountHive elementStat({TString("\'mode \'alter")});
- VerifyProgram(reqSetNull, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["\'mode \'alter"]);
- }
-
- Y_UNIT_TEST(AlterSequence) {
- UNIT_ASSERT(SqlToYql(R"(
- USE plato;
- ALTER SEQUENCE sequence START WITH 10 INCREMENT 2 RESTART WITH 5;
- )").IsOk());
- UNIT_ASSERT(SqlToYql(R"(
- USE plato;
- ALTER SEQUENCE sequence INCREMENT 2;
- )").IsOk());
- UNIT_ASSERT(SqlToYql(R"(
- USE plato;
- ALTER SEQUENCE sequence INCREMENT 2 START 1000;
- )").IsOk());
- UNIT_ASSERT(SqlToYql(R"(
- USE plato;
- ALTER SEQUENCE sequence RESTART START 1000;
- )").IsOk());
- UNIT_ASSERT(SqlToYql(R"(
- USE plato;
- ALTER SEQUENCE IF EXISTS sequence INCREMENT 1000 START 100 RESTART;
- )").IsOk());
- UNIT_ASSERT(SqlToYql(R"(
- USE plato;
- ALTER SEQUENCE IF EXISTS sequence RESTART 1000 START WITH 100 INCREMENT BY 7;
- )").IsOk());
- }
-
- Y_UNIT_TEST(AlterSequenceIncorrect) {
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence START WITH 10 INCREMENT 2 RESTART WITH 5 RESTART;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:75: Error: Restart value defined more than once\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence START WITH 10 INCREMENT 2 START 100 RESTART WITH 5;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:60: Error: Start value defined more than once\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence INCREMENT BY 7 START WITH 10 INCREMENT 2 RESTART WITH 5 RESTART;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:62: Error: Increment defined more than once\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 100 START WITH 10 INCREMENT 2 RESTART WITH 5;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:77: Error: Restart value defined more than once\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1234234543563435151456 START WITH 10 INCREMENT 2;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Error: Failed to parse number from string: 1234234543563435151456, number limit overflow\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1 START WITH 9223372036854775817 INCREMENT 4;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Start value: 9223372036854775817 cannot be greater than max value: 9223372036854775807\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 9223372036854775827 START WITH 5 INCREMENT 4;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Restart value: 9223372036854775827 cannot be greater than max value: 9223372036854775807\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1 START WITH 4 INCREMENT 0;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Increment must not be zero\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 0 START WITH 4 INCREMENT 1;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Restart value: 0 cannot be less than min value: 1\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1 START WITH 0 INCREMENT 1;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Start value: 0 cannot be less than min value: 1\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1 START WITH 1 INCREMENT 9223372036854775837;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Increment: 9223372036854775837 cannot be greater than max value: 9223372036854775807\n");
- }
- }
-
- Y_UNIT_TEST(AlterSequenceCorrect) {
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence START WITH 10 INCREMENT 2 RESTART WITH 5;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("sequence"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter"));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("alter_if_exists"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("start"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("increment"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("restart"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE IF EXISTS sequence INCREMENT 2 RESTART;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("sequence"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter_if_exists"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("increment"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("restart"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE IF EXISTS sequence START 10 INCREMENT BY 2;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("sequence"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter_if_exists"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("start"));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("restart"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("increment"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
- }
-
- Y_UNIT_TEST(ShowCreateTable) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- SHOW CREATE TABLE user;
- )");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Read") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("showCreateTable"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Read"), 0}, {TString("showCreateTable"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Read"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["showCreateTable"]);
- }
-
- Y_UNIT_TEST(OptionalAliases) {
- UNIT_ASSERT(SqlToYql("USE plato; SELECT foo FROM (SELECT key foo FROM Input);").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT a.x FROM Input1 a JOIN Input2 b ON a.key = b.key;").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT a.x FROM (VALUES (1,2), (3,4)) a(x,key) JOIN Input b ON a.key = b.key;").IsOk());
- }
-
- Y_UNIT_TEST(TableNameConstness) {
- UNIT_ASSERT(SqlToYql("USE plato; $path = 'foo'; SELECT TableName($path), count(*) FROM Input;").IsOk());
- UNIT_ASSERT(SqlToYql("$path = 'foo'; SELECT TableName($path, 'yt'), count(*) FROM plato.Input;").IsOk());
- ExpectFailWithError("USE plato; SELECT TableName(), count(*) FROM plato.Input;",
- "<main>:1:19: Error: Expression has to be an aggregation function or key column, because aggregation is used elsewhere in this subquery\n");
- }
-
- Y_UNIT_TEST(UseShouldWorkAsColumnName) {
- UNIT_ASSERT(SqlToYql("select use from (select 1 as use);").IsOk());
- }
-
- Y_UNIT_TEST(TrueFalseWorkAfterDollar) {
- UNIT_ASSERT(SqlToYql("$ true = false; SELECT $ true or false;").IsOk());
- UNIT_ASSERT(SqlToYql("$False = 0; SELECT $False;").IsOk());
- }
-
- Y_UNIT_TEST(WithSchemaEquals) {
- UNIT_ASSERT(SqlToYql("select * from plato.T with schema Struct<a:Int32, b:String>;").IsOk());
- UNIT_ASSERT(SqlToYql("select * from plato.T with columns = Struct<a:Int32, b:String>;").IsOk());
- }
-
- Y_UNIT_TEST(WithNonStructSchemaS3) {
- NSQLTranslation::TTranslationSettings settings;
- settings.ClusterMapping["s3bucket"] = NYql::S3ProviderName;
- UNIT_ASSERT(SqlToYqlWithSettings("select * from s3bucket.`foo` with schema (col1 Int32, String as col2, Int64 as col3);", settings).IsOk());
- }
-
- Y_UNIT_TEST(AllowNestedTuplesInGroupBy) {
- NYql::TAstParseResult res = SqlToYql("select count(*) from plato.Input group by 1 + (x, y, z);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Aggregate core '('\"group0\")"));
- };
-
- TWordCountHive elementStat({"Aggregate"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["Aggregate"] == 1);
- }
-
- Y_UNIT_TEST(AllowGroupByWithParens) {
- NYql::TAstParseResult res = SqlToYql("select count(*) from plato.Input group by (x, y as alias1, z);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Aggregate core '('\"x\" '\"alias1\" '\"z\")"));
- };
-
- TWordCountHive elementStat({"Aggregate"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["Aggregate"] == 1);
- }
-
- Y_UNIT_TEST(CreateAsyncReplicationParseCorrect) {
- auto req = R"(
- USE plato;
- CREATE ASYNC REPLICATION MyReplication
- FOR table1 AS table2, table3 AS table4
- WITH (
- CONNECTION_STRING = "grpc://localhost:2135/?database=/MyDatabase",
- ENDPOINT = "localhost:2135",
- DATABASE = "/MyDatabase"
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("MyReplication"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("create"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table1"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table2"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table3"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table4"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("connection_string"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("grpc://localhost:2135/?database=/MyDatabase"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("endpoint"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("localhost:2135"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("database"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("/MyDatabase"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateAsyncReplicationUnsupportedSettings) {
- auto reqTpl = R"(
- USE plato;
- CREATE ASYNC REPLICATION MyReplication
- FOR table1 AS table2, table3 AS table4
- WITH (
- %s = "%s"
- )
- )";
-
- auto settings = THashMap<TString, TString>{
- {"STATE", "DONE"},
- {"FAILOVER_MODE", "FORCE"},
- };
-
- for (const auto& [k, v] : settings) {
- auto req = Sprintf(reqTpl, k.c_str(), v.c_str());
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), Sprintf("<main>:6:%zu: Error: %s is not supported in CREATE\n", 20 + k.size(), k.c_str()));
- }
- }
-
- Y_UNIT_TEST(AsyncReplicationInvalidCommitInterval) {
- auto req = R"(
- USE plato;
- CREATE ASYNC REPLICATION MyReplication
- FOR table1 AS table2, table3 AS table4
- WITH (
- COMMIT_INTERVAL = "FOO"
- );
- )";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:6:35: Error: Literal of Interval type is expected for COMMIT_INTERVAL\n");
- }
-
- Y_UNIT_TEST(AlterAsyncReplicationParseCorrect) {
- auto req = R"(
- USE plato;
- ALTER ASYNC REPLICATION MyReplication
- SET (
- STATE = "DONE",
- FAILOVER_MODE = "FORCE"
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("MyReplication"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("state"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("DONE"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("failover_mode"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("FORCE"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AlterAsyncReplicationSettings) {
- auto reqTpl = R"(
- USE plato;
- ALTER ASYNC REPLICATION MyReplication
- SET (
- %s = "%s"
- )
- )";
-
- auto settings = THashMap<TString, TString>{
- {"connection_string", "grpc://localhost:2135/?database=/MyDatabase"},
- {"endpoint", "localhost:2135"},
- {"database", "/MyDatabase"},
- {"token", "foo"},
- {"token_secret_name", "foo_secret_name"},
- {"user", "user"},
- {"password", "bar"},
- {"password_secret_name", "bar_secret_name"},
- };
-
- for (const auto& [k, v] : settings) {
- auto req = Sprintf(reqTpl, k.c_str(), v.c_str());
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&k, &v](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("MyReplication"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(k));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(v));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
- }
-
- Y_UNIT_TEST(AlterAsyncReplicationUnsupportedSettings) {
- {
- auto req = R"(
- USE plato;
- ALTER ASYNC REPLICATION MyReplication SET (CONSISTENCY_LEVEL = "GLOBAL");
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:80: Error: CONSISTENCY_LEVEL is not supported in ALTER\n");
- }
- {
- auto req = R"(
- USE plato;
- ALTER ASYNC REPLICATION MyReplication SET (COMMIT_INTERVAL = Interval("PT10S"));
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:87: Error: COMMIT_INTERVAL is not supported in ALTER\n");
- }
- }
-
- Y_UNIT_TEST(AsyncReplicationInvalidSettings) {
- auto req = R"(
- USE plato;
- ALTER ASYNC REPLICATION MyReplication SET (FOO = "BAR");
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:62: Error: Unknown replication setting: FOO\n");
- }
-
- Y_UNIT_TEST(DropAsyncReplicationParseCorrect) {
- auto req = R"(
- USE plato;
- DROP ASYNC REPLICATION MyReplication;
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("MyReplication"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropAsyncReplicationCascade) {
- auto req = R"(
- USE plato;
- DROP ASYNC REPLICATION MyReplication CASCADE;
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropCascade"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(PragmaCompactGroupBy) {
- auto req = "PRAGMA CompactGroupBy; SELECT key, COUNT(*) FROM plato.Input GROUP BY key;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Aggregate") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('compact)"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Aggregate"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Aggregate"]);
- }
-
- Y_UNIT_TEST(PragmaDisableCompactGroupBy) {
- auto req = "PRAGMA DisableCompactGroupBy; SELECT key, COUNT(*) FROM plato.Input GROUP /*+ compact() */ BY key;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Aggregate") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'('compact)"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Aggregate"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Aggregate"]);
- }
-
- Y_UNIT_TEST(AutoSampleWorksWithNamedSubquery) {
- UNIT_ASSERT(SqlToYql("$src = select * from plato.Input; select * from $src sample 0.2").IsOk());
- }
-
- Y_UNIT_TEST(AutoSampleWorksWithSubquery) {
- UNIT_ASSERT(SqlToYql("select * from (select * from plato.Input) sample 0.2").IsOk());
- }
-
- Y_UNIT_TEST(CreateTableTrailingComma) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE tableName (Key Uint32, PRIMARY KEY (Key),);").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE tableName (Key Uint32,);").IsOk());
- }
-
- Y_UNIT_TEST(BetweenSymmetric) {
- UNIT_ASSERT(SqlToYql("select 3 between symmetric 5 and 4;").IsOk());
- UNIT_ASSERT(SqlToYql("select 3 between asymmetric 5 and 4;").IsOk());
- UNIT_ASSERT(SqlToYql("use plato; select key between symmetric and and and from Input;").IsOk());
- UNIT_ASSERT(SqlToYql("use plato; select key between and and and from Input;").IsOk());
- }
-}
-
-Y_UNIT_TEST_SUITE(ExternalFunction) {
- Y_UNIT_TEST(ValidUseFunctions) {
-
- UNIT_ASSERT(SqlToYql(
- "PROCESS plato.Input"
- " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo', <|a: 123, b: a + 641|>)"
- " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,"
- " CONCURRENCY=3, OPTIMIZE_FOR='CALLS'").IsOk());
-
- // use CALLS without quotes, as keyword
- UNIT_ASSERT(SqlToYql(
- "PROCESS plato.Input"
- " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo')"
- " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,"
- " OPTIMIZE_FOR=CALLS").IsOk());
-
- UNIT_ASSERT(SqlToYql(
- "PROCESS plato.Input"
- " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo', TableRow())"
- " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,"
- " CONCURRENCY=3").IsOk());
-
- UNIT_ASSERT(SqlToYql(
- "PROCESS plato.Input"
- " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo')"
- " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,"
- " CONCURRENCY=3, BATCH_SIZE=1000000, CONNECTION='yc-folder34fse-con',"
- " INIT=[0, 900]").IsOk());
-
- UNIT_ASSERT(SqlToYql(
- "PROCESS plato.Input"
- " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'bar', TableRow())"
- " WITH UNKNOWN_PARAM_1='837747712', UNKNOWN_PARAM_2=Tuple<Uint16, Utf8>,"
- " INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>").IsOk());
- }
-
-
- Y_UNIT_TEST(InValidUseFunctions) {
- ExpectFailWithError("PROCESS plato.Input USING some::udf(*) WITH INPUT_TYPE=Struct<a:Int32>",
- "<main>:1:33: Error: PROCESS without USING EXTERNAL FUNCTION doesn't allow WITH block\n");
-
- ExpectFailWithError("PROCESS plato.Input USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'jhhjfh88134d')"
- " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>"
- " ASSUME ORDER BY key",
- "<main>:1:129: Error: PROCESS with USING EXTERNAL FUNCTION doesn't allow ASSUME block\n");
-
- ExpectFailWithError("PROCESS plato.Input USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo', 'bar', 'baz')",
- "<main>:1:15: Error: EXTERNAL FUNCTION requires from 2 to 3 arguments, but got: 4\n");
-
- ExpectFailWithError("PROCESS plato.Input\n"
- " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo', <|field_1: a1, field_b: b1|>)\n"
- " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,\n"
- " CONCURRENCY=3, BATCH_SIZE=1000000, CONNECTION='yc-folder34fse-con',\n"
- " CONCURRENCY=5, INPUT_TYPE=Struct<b:Bool>,\n"
- " INIT=[0, 900]\n",
- "<main>:5:2: Error: WITH \"CONCURRENCY\" clause should be specified only once\n"
- "<main>:5:17: Error: WITH \"INPUT_TYPE\" clause should be specified only once\n");
- }
-}
-
-Y_UNIT_TEST_SUITE(SqlToYQLErrors) {
- Y_UNIT_TEST(UdfSyntaxSugarMissingCall) {
- auto req = "SELECT Udf(DateTime::FromString, \"foo\" as RunConfig);";
- auto res = SqlToYql(req);
- TString a1 = Err2Str(res);
- TString a2("<main>:1:8: Error: Abstract Udf Node can't be used as a part of expression.\n");
- UNIT_ASSERT_NO_DIFF(a1, a2);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarIsNotCallable) {
- auto req = "SELECT Udf(123, \"foo\" as RunConfig);";
- auto res = SqlToYql(req);
- TString a1 = Err2Str(res);
- TString a2("<main>:1:8: Error: Udf: first argument must be a callable, like Foo::Bar\n");
- UNIT_ASSERT_NO_DIFF(a1, a2);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarNoArgs) {
- auto req = "SELECT Udf()();";
- auto res = SqlToYql(req);
- TString a1 = Err2Str(res);
- TString a2("<main>:1:8: Error: Udf: expected at least one argument\n");
- UNIT_ASSERT_NO_DIFF(a1, a2);
- }
-
- Y_UNIT_TEST(StrayUTF8) {
- /// 'c' in plato is russian here
- NYql::TAstParseResult res = SqlToYql("select * from сedar.Input");
- UNIT_ASSERT(!res.Root);
-
- TString a1 = Err2Str(res);
- TString a2(R"foo(<main>:1:14: Error: Unexpected character 'с' (Unicode character <1089>) : cannot match to any predicted input...
-
-<main>:1:15: Error: Unexpected character : cannot match to any predicted input...
-
-)foo");
-
- UNIT_ASSERT_NO_DIFF(a1, a2);
- }
-
- Y_UNIT_TEST(IvalidStringLiteralWithEscapedBackslash) {
- NYql::TAstParseResult res1 = SqlToYql(R"foo($bar = 'a\\'b';)foo");
- NYql::TAstParseResult res2 = SqlToYql(R"foo($bar = "a\\"b";)foo");
- UNIT_ASSERT(!res1.Root);
- UNIT_ASSERT(!res2.Root);
-
- UNIT_ASSERT_NO_DIFF(Err2Str(res1), "<main>:1:15: Error: Unexpected character : syntax error...\n\n");
- UNIT_ASSERT_NO_DIFF(Err2Str(res2), "<main>:1:15: Error: Unexpected character : syntax error...\n\n");
- }
-
- Y_UNIT_TEST(InvalidHexInStringLiteral) {
- NYql::TAstParseResult res = SqlToYql("select \"foo\\x1\\xfe\"");
- UNIT_ASSERT(!res.Root);
- TString a1 = Err2Str(res);
- TString a2 = "<main>:1:15: Error: Failed to parse string literal: Invalid hexadecimal value\n";
-
- UNIT_ASSERT_NO_DIFF(a1, a2);
- }
-
- Y_UNIT_TEST(InvalidOctalInMultilineStringLiteral) {
- NYql::TAstParseResult res = SqlToYql("select \"foo\n"
- "bar\n"
- "\\01\"");
- UNIT_ASSERT(!res.Root);
- TString a1 = Err2Str(res);
- TString a2 = "<main>:3:4: Error: Failed to parse string literal: Invalid octal value\n";
-
- UNIT_ASSERT_NO_DIFF(a1, a2);
- }
-
- Y_UNIT_TEST(InvalidDoubleAtString) {
- NYql::TAstParseResult res = SqlToYql("select @@@@@@");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:13: Error: Unexpected character : syntax error...\n\n");
- }
-
- Y_UNIT_TEST(InvalidDoubleAtStringWhichWasAcceptedEarlier) {
- NYql::TAstParseResult res = SqlToYql("SELECT @@foo@@ @ @@bar@@");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:7: Error: Unexpected token '@@foo@@' : cannot match to any predicted input...\n\n");
- }
-
- Y_UNIT_TEST(InvalidStringFromTable) {
- NYql::TAstParseResult res = SqlToYql("select \"FOO\"\"BAR from plato.foo");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:31: Error: Unexpected character : syntax error...\n\n");
- }
-
- Y_UNIT_TEST(InvalidDoubleAtStringFromTable) {
- NYql::TAstParseResult res = SqlToYql("select @@@@@@ from plato.foo");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:28: Error: Unexpected character : syntax error...\n\n");
- }
-
- Y_UNIT_TEST(SelectInvalidSyntax) {
- NYql::TAstParseResult res = SqlToYql("select 1 form Wat");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:14: Error: Unexpected token 'Wat' : cannot match to any predicted input...\n\n");
- }
-
- Y_UNIT_TEST(SelectNoCluster) {
- NYql::TAstParseResult res = SqlToYql("select foo from bar");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: No cluster name given and no default cluster is selected\n");
- }
-
- Y_UNIT_TEST(SelectDuplicateColumns) {
- NYql::TAstParseResult res = SqlToYql("select a, a from plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:11: Error: Unable to use duplicate column names. Collision in name: a\n");
- }
-
- Y_UNIT_TEST(SelectDuplicateLabels) {
- NYql::TAstParseResult res = SqlToYql("select a as foo, b as foo from plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:18: Error: Unable to use duplicate column names. Collision in name: foo\n");
- }
-
- Y_UNIT_TEST(SelectCaseWithoutThen) {
- NYql::TAstParseResult res = SqlToYql("select case when true 1;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:1:22: Error: Unexpected token absence : Missing THEN \n\n"
- "<main>:1:23: Error: Unexpected token absence : Missing END \n\n"
- );
- }
-
- Y_UNIT_TEST(SelectComplexCaseWithoutThen) {
- NYql::TAstParseResult res = SqlToYql(
- "SELECT *\n"
- "FROM plato.Input AS a\n"
- "WHERE CASE WHEN a.key = \"foo\" a.subkey ELSE a.value END\n"
- );
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:30: Error: Unexpected token absence : Missing THEN \n\n");
- }
-
- Y_UNIT_TEST(SelectCaseWithoutEnd) {
- NYql::TAstParseResult res = SqlToYql("select case a when b then c end from plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: ELSE is required\n");
- }
-
- Y_UNIT_TEST(SelectWithBadAggregationNoInput) {
- NYql::TAstParseResult res = SqlToYql("select a, Min(b), c");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:8: Error: Column reference 'a'\n"
- "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:15: Error: Column reference 'b'\n"
- "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:19: Error: Column reference 'c'\n"
- );
- }
-
- Y_UNIT_TEST(SelectWithBadAggregation) {
- ExpectFailWithError("select count(*), 1 + key from plato.Input",
- "<main>:1:22: Error: Column `key` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(SelectWithBadAggregatedTerms) {
- ExpectFailWithError("select key, 2 * subkey from plato.Input group by key",
- "<main>:1:17: Error: Column `subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(SelectDistinctWithBadAggregation) {
- ExpectFailWithError("select distinct count(*), 1 + key from plato.Input",
- "<main>:1:31: Error: Column `key` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- ExpectFailWithError("select distinct key, 2 * subkey from plato.Input group by key",
- "<main>:1:26: Error: Column `subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(SelectWithBadAggregationInHaving) {
- ExpectFailWithError("select key from plato.Input group by key\n"
- "having \"f\" || value == \"foo\"",
- "<main>:2:15: Error: Column `value` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(JoinWithNonAggregatedColumnInProjection) {
- ExpectFailWithError("select a.key, 1 + b.subkey\n"
- "from plato.Input1 as a join plato.Input2 as b using(key)\n"
- "group by a.key;",
- "<main>:1:19: Error: Column `b.subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
-
- ExpectFailWithError("select a.key, 1 + b.subkey.x\n"
- "from plato.Input1 as a join plato.Input2 as b using(key)\n"
- "group by a.key;",
- "<main>:1:19: Error: Column must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(SelectWithBadAggregatedTermsWithSources) {
- ExpectFailWithError("select key, 1 + a.subkey\n"
- "from plato.Input1 as a\n"
- "group by a.key;",
- "<main>:1:17: Error: Column `a.subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- ExpectFailWithError("select key, 1 + a.subkey.x\n"
- "from plato.Input1 as a\n"
- "group by a.key;",
- "<main>:1:17: Error: Column must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(WarnForAggregationBySelectAlias) {
- NYql::TAstParseResult res = SqlToYql("select c + 1 as c from plato.Input\n"
- "group by c");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:2:11: Warning: GROUP BY will aggregate by column `c` instead of aggregating by SELECT expression with same alias, code: 4532\n"
- "<main>:1:10: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n");
-
- res = SqlToYql("select c + 1 as c from plato.Input\n"
- "group by Math::Floor(c + 2) as c;");
-
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:2:22: Warning: GROUP BY will aggregate by column `c` instead of aggregating by SELECT expression with same alias, code: 4532\n"
- "<main>:1:10: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n");
- }
-
- Y_UNIT_TEST(NoWarnForAggregationBySelectAliasWhenAggrFunctionsAreUsedInAlias) {
- NYql::TAstParseResult res = SqlToYql("select\n"
- " cast(avg(val) as int) as value,\n"
- " value as key\n"
- "from\n"
- " plato.Input\n"
- "group by value");
-
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- res = SqlToYql("select\n"
- " cast(avg(val) over w as int) as value,\n"
- " value as key\n"
- "from\n"
- " plato.Input\n"
- "group by value\n"
- "window w as ()");
-
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(NoWarnForAggregationBySelectAliasWhenQualifiedNameIsUsed) {
- NYql::TAstParseResult res = SqlToYql("select\n"
- " Unwrap(a.key) as key\n"
- "from plato.Input as a\n"
- "join plato.Input2 as b using(k)\n"
- "group by a.key;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- res = SqlToYql("select Unwrap(a.key) as key\n"
- "from plato.Input as a\n"
- "group by a.key;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(NoWarnForAggregationBySelectAliasWhenTrivialRenamingIsUsed) {
- NYql::TAstParseResult res = SqlToYql("select a.key as key\n"
- "from plato.Input as a\n"
- "group by key;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- res = SqlToYql("select key as key\n"
- "from plato.Input\n"
- "group by key;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(ErrorByAggregatingByExpressionWithSameExpressionInSelect) {
- ExpectFailWithError("select k * 2 from plato.Input group by k * 2",
- "<main>:1:8: Error: Column `k` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(ErrorForAggregationBySelectAlias) {
- ExpectFailWithError("select key, Math::Floor(1.1 + a.subkey) as foo\n"
- "from plato.Input as a\n"
- "group by a.key, foo;",
- "<main>:3:17: Warning: GROUP BY will aggregate by column `foo` instead of aggregating by SELECT expression with same alias, code: 4532\n"
- "<main>:1:19: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n"
- "<main>:1:31: Error: Column `a.subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
-
- ExpectFailWithError("select c + 1 as c from plato.Input\n"
- "group by Math::Floor(c + 2);",
- "<main>:2:22: Warning: GROUP BY will aggregate by column `c` instead of aggregating by SELECT expression with same alias, code: 4532\n"
- "<main>:1:10: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n"
- "<main>:1:8: Error: Column `c` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(ExplainQueryPlan) {
- NYql::TAstParseResult res = SqlToYql("EXPLAIN Q U E R Y PLAN SELECT 1;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "<main>:1:8: Error: Unexpected token 'Q' : cannot match to any predicted input");
- }
-
- Y_UNIT_TEST(SelectWithDuplicateGroupingColumns) {
- NYql::TAstParseResult res = SqlToYql("select c from plato.Input group by c, c");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Duplicate grouping column: c\n");
- }
-
- Y_UNIT_TEST(SelectWithBadAggregationInGrouping) {
- NYql::TAstParseResult res = SqlToYql("select a, Min(b), c group by c");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:30: Error: Column reference 'c'\n");
- }
-
- Y_UNIT_TEST(SelectWithOpOnBadAggregation) {
- ExpectFailWithError("select 1 + a + Min(b) from plato.Input",
- "<main>:1:12: Error: Column `a` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(SelectOrderByConstantNum) {
- NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by 1");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:36: Error: Unable to ORDER BY constant expression\n");
- }
-
- Y_UNIT_TEST(SelectOrderByConstantExpr) {
- NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by 1 * 42");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:38: Error: Unable to ORDER BY constant expression\n");
- }
-
- Y_UNIT_TEST(SelectOrderByConstantString) {
- NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by \"nest\"");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:36: Error: Unable to ORDER BY constant expression\n");
- }
-
- Y_UNIT_TEST(SelectOrderByAggregated) {
- NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by min(a)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:36: Error: Unable to ORDER BY aggregated values\n");
- }
-
- Y_UNIT_TEST(ErrorInOrderByExpresison) {
- NYql::TAstParseResult res = SqlToYql("select key, value from plato.Input order by (key as zey)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:45: Error: You should use in ORDER BY column name, qualified field, callable function or expression\n");
- }
-
- Y_UNIT_TEST(ErrorsInOrderByWhenColumnIsMissingInProjection) {
- ExpectFailWithError("select subkey from (select 1 as subkey) order by key", "<main>:1:50: Error: Column key is not in source column set\n");
- ExpectFailWithError("select subkey from plato.Input as a order by x.key", "<main>:1:46: Error: Unknown correlation name: x\n");
- ExpectFailWithError("select distinct a, b from plato.Input order by c", "<main>:1:48: Error: Column c is not in source column set. Did you mean a?\n");
- ExpectFailWithError("select count(*) as a from plato.Input order by c", "<main>:1:48: Error: Column c is not in source column set. Did you mean a?\n");
- ExpectFailWithError("select count(*) as a, b, from plato.Input group by b order by c", "<main>:1:63: Error: Column c is not in source column set. Did you mean a?\n");
- UNIT_ASSERT(SqlToYql("select a, b from plato.Input order by c").IsOk());
- }
-
- Y_UNIT_TEST(SelectAggregatedWhere) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input where count(key)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:33: Error: Can not use aggregated values in filtering\n");
- }
-
- Y_UNIT_TEST(DoubleFrom) {
- NYql::TAstParseResult res = SqlToYql("from plato.Input select * from plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:27: Error: Only one FROM clause is allowed\n");
- }
-
- Y_UNIT_TEST(SelectJoinMissingCorrName) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input1 as a join plato.Input2 as b on a.key == key");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:65: Error: JOIN: column requires correlation name\n");
- }
-
- Y_UNIT_TEST(SelectJoinMissingCorrName1) {
- NYql::TAstParseResult res = SqlToYql(
- "use plato;\n"
- "$foo = select * from Input1;\n"
- "select * from Input2 join $foo USING(key);\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:27: Error: JOIN: missing correlation name for source\n");
- }
-
- Y_UNIT_TEST(SelectJoinMissingCorrName2) {
- NYql::TAstParseResult res = SqlToYql(
- "use plato;\n"
- "$foo = select * from Input1;\n"
- "select * from Input2 cross join $foo;\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:33: Error: JOIN: missing correlation name for source\n");
- }
-
- Y_UNIT_TEST(SelectJoinEmptyCorrNames) {
- NYql::TAstParseResult res = SqlToYql(
- "$left = (SELECT * FROM plato.Input1 LIMIT 2);\n"
- "$right = (SELECT * FROM plato.Input2 LIMIT 2);\n"
- "SELECT * FROM $left FULL JOIN $right USING (key);\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:45: Error: At least one correlation name is required in join\n");
- }
-
- Y_UNIT_TEST(SelectJoinSameCorrNames) {
- NYql::TAstParseResult res = SqlToYql("SELECT Input.key FROM plato.Input JOIN plato.Input1 ON Input.key == Input.subkey\n");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:66: Error: JOIN: different correlation names are required for joined tables\n");
- }
-
- Y_UNIT_TEST(SelectJoinConstPredicateArg) {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input1 as A JOIN plato.Input2 as B ON A.key == B.key AND A.subkey == \"wtf\"\n");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:87: Error: JOIN: each equality predicate argument must depend on exactly one JOIN input\n");
- }
-
- Y_UNIT_TEST(SelectJoinNonEqualityPredicate) {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input1 as A JOIN plato.Input2 as B ON A.key == B.key AND A.subkey > B.subkey\n");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:87: Error: JOIN ON expression must be a conjunction of equality predicates\n");
- }
-
- Y_UNIT_TEST(SelectEquiJoinCorrNameOutOfScope) {
- NYql::TAstParseResult res = SqlToYql(
- "PRAGMA equijoin;\n"
- "SELECT * FROM plato.A JOIN plato.B ON A.key == C.key JOIN plato.C ON A.subkey == C.subkey;\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:45: Error: JOIN: can not use source: C in equality predicate, it is out of current join scope\n");
- }
-
- Y_UNIT_TEST(SelectEquiJoinNoRightSource) {
- NYql::TAstParseResult res = SqlToYql(
- "PRAGMA equijoin;\n"
- "SELECT * FROM plato.A JOIN plato.B ON A.key == B.key JOIN plato.C ON A.subkey == B.subkey;\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:79: Error: JOIN ON equality predicate must have one of its arguments from the rightmost source\n");
- }
-
- Y_UNIT_TEST(SelectEquiJoinOuterWithoutType) {
- NYql::TAstParseResult res = SqlToYql(
- "SELECT * FROM plato.A Outer JOIN plato.B ON A.key == B.key;\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:23: Error: Invalid join type: OUTER JOIN. OUTER keyword is optional and can only be used after LEFT, RIGHT or FULL\n");
- }
-
- Y_UNIT_TEST(SelectEquiJoinOuterWithWrongType) {
- NYql::TAstParseResult res = SqlToYql(
- "SELECT * FROM plato.A LEFT semi OUTER JOIN plato.B ON A.key == B.key;\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:33: Error: Invalid join type: LEFT SEMI OUTER JOIN. OUTER keyword is optional and can only be used after LEFT, RIGHT or FULL\n");
- }
-
- Y_UNIT_TEST(InsertNoCluster) {
- NYql::TAstParseResult res = SqlToYql("insert into Output (foo) values (1)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: No cluster name given and no default cluster is selected\n");
- }
-
- Y_UNIT_TEST(InsertValuesNoLabels) {
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output values (1)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: INSERT INTO ... VALUES requires specification of table columns\n");
- }
-
- Y_UNIT_TEST(UpsertValuesNoLabelsKikimr) {
- NYql::TAstParseResult res = SqlToYql("upsert into plato.Output values (1)", 10, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: UPSERT INTO ... VALUES requires specification of table columns\n");
- }
-
- Y_UNIT_TEST(ReplaceValuesNoLabelsKikimr) {
- NYql::TAstParseResult res = SqlToYql("replace into plato.Output values (1)", 10, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:20: Error: REPLACE INTO ... VALUES requires specification of table columns\n");
- }
-
- Y_UNIT_TEST(InsertValuesInvalidLabels) {
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output (foo) values (1, 2)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:27: Error: VALUES have 2 columns, INSERT INTO expects: 1\n");
- }
-
- Y_UNIT_TEST(BuiltinFileOpNoArgs) {
- NYql::TAstParseResult res = SqlToYql("select FilePath()");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: FilePath() requires exactly 1 arguments, given: 0\n");
- }
-
- Y_UNIT_TEST(ProcessWithHaving) {
- NYql::TAstParseResult res = SqlToYql("process plato.Input using some::udf(value) having value == 1");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:15: Error: PROCESS does not allow HAVING yet! You may request it on yql@ maillist.\n");
- }
-
- Y_UNIT_TEST(ReduceNoBy) {
- NYql::TAstParseResult res = SqlToYql("reduce plato.Input using some::udf(value)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: Unexpected token absence : Missing ON \n\n<main>:1:25: Error: Unexpected token absence : Missing USING \n\n");
- }
-
- Y_UNIT_TEST(ReduceDistinct) {
- NYql::TAstParseResult res = SqlToYql("reduce plato.Input on key using some::udf(distinct value)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:43: Error: DISTINCT can not be used in PROCESS/REDUCE\n");
- }
-
- Y_UNIT_TEST(CreateTableWithView) {
- NYql::TAstParseResult res = SqlToYql("CREATE TABLE plato.foo:bar (key INT);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:22: Error: Unexpected token ':' : syntax error...\n\n");
- }
-
- Y_UNIT_TEST(AsteriskWithSomethingAfter) {
- NYql::TAstParseResult res = SqlToYql("select *, LENGTH(value) from plato.Input;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Unable to use plain '*' with other projection items. Please use qualified asterisk instead: '<table>.*' (<table> can be either table name or table alias).\n");
- }
-
- Y_UNIT_TEST(AsteriskWithSomethingBefore) {
- NYql::TAstParseResult res = SqlToYql("select LENGTH(value), * from plato.Input;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:23: Error: Unable to use plain '*' with other projection items. Please use qualified asterisk instead: '<table>.*' (<table> can be either table name or table alias).\n");
- }
-
- Y_UNIT_TEST(DuplicatedQualifiedAsterisk) {
- NYql::TAstParseResult res = SqlToYql("select in.*, key, in.* from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: Unable to use twice same quialified asterisk. Invalid source: in\n");
- }
-
- Y_UNIT_TEST(BrokenLabel) {
- NYql::TAstParseResult res = SqlToYql("select in.*, key as `funny.label` from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:14: Error: Unable to use '.' in column name. Invalid column name: funny.label\n");
- }
-
- Y_UNIT_TEST(KeyConflictDetect0) {
- NYql::TAstParseResult res = SqlToYql("select key, in.key as key from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:13: Error: Unable to use duplicate column names. Collision in name: key\n");
- }
-
- Y_UNIT_TEST(KeyConflictDetect1) {
- NYql::TAstParseResult res = SqlToYql("select length(key) as key, key from plato.Input;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:28: Error: Unable to use duplicate column names. Collision in name: key\n");
- }
-
- Y_UNIT_TEST(KeyConflictDetect2) {
- NYql::TAstParseResult res = SqlToYql("select key, in.key from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
- }
-
- Y_UNIT_TEST(AutogenerationAliasWithCollisionConflict1) {
- UNIT_ASSERT(SqlToYql("select LENGTH(Value), key as column0 from plato.Input;").IsOk());
- }
-
- Y_UNIT_TEST(AutogenerationAliasWithCollisionConflict2) {
- UNIT_ASSERT(SqlToYql("select key as column1, LENGTH(Value) from plato.Input;").IsOk());
- }
-
- Y_UNIT_TEST(MissedSourceTableForQualifiedAsteriskOnSimpleSelect) {
- NYql::TAstParseResult res = SqlToYql("use plato; select Intop.*, Input.key from plato.Input;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: Unknown correlation name: Intop\n");
- }
-
- Y_UNIT_TEST(MissedSourceTableForQualifiedAsteriskOnJoin) {
- NYql::TAstParseResult res = SqlToYql("use plato; select tmissed.*, t2.*, t1.key from plato.Input as t1 join plato.Input as t2 on t1.key==t2.key;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: Unknown correlation name for asterisk: tmissed\n");
- }
-
- Y_UNIT_TEST(UnableToReferenceOnNotExistSubcolumn) {
- NYql::TAstParseResult res = SqlToYql("select b.subkey from (select key from plato.Input as a) as b;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Column subkey is not in source column set\n");
- }
-
- Y_UNIT_TEST(ConflictOnSameNameWithQualify0) {
- NYql::TAstParseResult res = SqlToYql("select in.key, in.key as key from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
- }
-
- Y_UNIT_TEST(ConflictOnSameNameWithQualify1) {
- NYql::TAstParseResult res = SqlToYql("select in.key, length(key) as key from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
- }
-
- Y_UNIT_TEST(ConflictOnSameNameWithQualify2) {
- NYql::TAstParseResult res = SqlToYql("select key, in.key from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
- }
-
- Y_UNIT_TEST(ConflictOnSameNameWithQualify3) {
- NYql::TAstParseResult res = SqlToYql("select in.key, subkey as key from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
- }
-
- Y_UNIT_TEST(SelectFlattenBySameColumns) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key, key as kk)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:46: Error: Duplicate column name found: key in FlattenBy section\n");
- }
-
- Y_UNIT_TEST(SelectFlattenBySameAliases) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key as kk, subkey as kk);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Error: Duplicate alias found: kk in FlattenBy section\n");
- }
-
- Y_UNIT_TEST(SelectFlattenByExprSameAliases) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key as kk, ListSkip(subkey,1) as kk);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Error: Collision between alias and column name: kk in FlattenBy section\n");
- }
-
- Y_UNIT_TEST(SelectFlattenByConflictNameAndAlias0) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key, subkey as key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:46: Error: Collision between alias and column name: key in FlattenBy section\n");
- }
-
- Y_UNIT_TEST(SelectFlattenByConflictNameAndAlias1) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key as kk, subkey as key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Error: Collision between alias and column name: key in FlattenBy section\n");
- }
-
- Y_UNIT_TEST(SelectFlattenByExprConflictNameAndAlias1) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key as kk, ListSkip(subkey,1) as key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Error: Duplicate column name found: key in FlattenBy section\n");
- }
-
- Y_UNIT_TEST(SelectFlattenByUnnamedExpr) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key, ListSkip(key, 1))");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:46: Error: Unnamed expression after FLATTEN BY is not allowed\n");
- }
-
- Y_UNIT_TEST(UseInOnStrings) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input where \"foo\" in \"foovalue\";");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:42: Error: Unable to use IN predicate with string argument, it won't search substring - "
- "expecting tuple, list, dict or single column table source\n");
- }
-
- Y_UNIT_TEST(UseSubqueryInScalarContextInsideIn) {
- NYql::TAstParseResult res = SqlToYql("$q = (select key from plato.Input); select * from plato.Input where subkey in ($q);");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:79: Warning: Using subrequest in scalar context after IN, "
- "perhaps you should remove parenthesis here, code: 4501\n");
- }
-
- Y_UNIT_TEST(InHintsWithKeywordClash) {
- NYql::TAstParseResult res = SqlToYql("SELECT COMPACT FROM plato.Input WHERE COMPACT IN COMPACT `COMPACT`(1,2,3)");
- UNIT_ASSERT(!res.Root);
- // should try to parse last compact as call expression
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:58: Error: Unknown builtin: COMPACT\n");
- }
-
- Y_UNIT_TEST(ErrorColumnPosition) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato;\n"
- "SELECT \n"
- "value FROM (\n"
- "select key from Input\n"
- ");\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:1: Error: Column value is not in source column set\n");
- }
-
- Y_UNIT_TEST(PrimaryViewAbortMapReduce) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input VIEW PRIMARY KEY");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: primary view is not supported for yt tables\n");
- }
-
- Y_UNIT_TEST(InsertAbortMapReduce) {
- NYql::TAstParseResult res = SqlToYql("INSERT OR ABORT INTO plato.Output SELECT key FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: INSERT OR ABORT INTO is not supported for yt tables\n");
- }
-
- Y_UNIT_TEST(ReplaceIntoMapReduce) {
- NYql::TAstParseResult res = SqlToYql("REPLACE INTO plato.Output SELECT key FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: Meaning of REPLACE INTO has been changed, now you should use INSERT INTO <table> WITH TRUNCATE ... for yt\n");
- }
-
- Y_UNIT_TEST(UpsertIntoMapReduce) {
- NYql::TAstParseResult res = SqlToYql("UPSERT INTO plato.Output SELECT key FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: UPSERT INTO is not supported for yt tables\n");
- }
-
- Y_UNIT_TEST(UpdateMapReduce) {
- NYql::TAstParseResult res = SqlToYql("UPDATE plato.Output SET value = value + 1 WHERE key < 1");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: UPDATE is unsupported for yt\n");
- }
-
- Y_UNIT_TEST(DeleteMapReduce) {
- NYql::TAstParseResult res = SqlToYql("DELETE FROM plato.Output WHERE key < 1");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: DELETE is unsupported for yt\n");
- }
-
- Y_UNIT_TEST(ReplaceIntoWithTruncate) {
- NYql::TAstParseResult res = SqlToYql("REPLACE INTO plato.Output WITH TRUNCATE SELECT key FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:32: Error: Unable REPLACE INTO with truncate mode\n");
- }
-
- Y_UNIT_TEST(UpsertIntoWithTruncate) {
- NYql::TAstParseResult res = SqlToYql("UPSERT INTO plato.Output WITH TRUNCATE SELECT key FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:31: Error: Unable UPSERT INTO with truncate mode\n");
- }
-
- Y_UNIT_TEST(InsertIntoWithTruncateKikimr) {
- NYql::TAstParseResult res = SqlToYql("INSERT INTO plato.Output WITH TRUNCATE SELECT key FROM plato.Input", 10, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: INSERT INTO WITH TRUNCATE is not supported for kikimr tables\n");
- }
-
- Y_UNIT_TEST(InsertIntoWithWrongArgumentCount) {
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output with truncate (key, value, subkey) values (5, '1', '2', '3');");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:53: Error: VALUES have 4 columns, INSERT INTO ... WITH TRUNCATE expects: 3\n");
- }
-
- Y_UNIT_TEST(UpsertWithWrongArgumentCount) {
- NYql::TAstParseResult res = SqlToYql("upsert into plato.Output (key, value, subkey) values (2, '3');", 10, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:39: Error: VALUES have 2 columns, UPSERT INTO expects: 3\n");
- }
-
- Y_UNIT_TEST(GroupingSetByExprWithoutAlias) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY GROUPING SETS (cast(key as uint32), subkey);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:53: Error: Unnamed expressions are not supported in GROUPING SETS. Please use '<expr> AS <name>'.\n");
- }
-
- Y_UNIT_TEST(GroupingSetByExprWithoutAlias2) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY subkey || subkey, GROUPING SETS (\n"
- "cast(key as uint32), subkey);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:1: Error: Unnamed expressions are not supported in GROUPING SETS. Please use '<expr> AS <name>'.\n");
- }
-
- Y_UNIT_TEST(CubeByExprWithoutAlias) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY CUBE (key, subkey / key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:56: Error: Unnamed expressions are not supported in CUBE. Please use '<expr> AS <name>'.\n");
- }
-
- Y_UNIT_TEST(RollupByExprWithoutAlias) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY ROLLUP (subkey / key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:53: Error: Unnamed expressions are not supported in ROLLUP. Please use '<expr> AS <name>'.\n");
- }
-
- Y_UNIT_TEST(GroupByHugeCubeDeniedNoPragma) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY CUBE (key, subkey, value, key + subkey as sum, key - subkey as sub, key + val as keyval);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:119: Error: GROUP BY CUBE is allowed only for 5 columns, but you use 6\n");
- }
-
- Y_UNIT_TEST(GroupByInvalidPragma) {
- NYql::TAstParseResult res = SqlToYql("PRAGMA GroupByCubeLimit = '-4';");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:27: Error: Expected unsigned integer literal as a single argument for: GroupByCubeLimit\n");
- }
-
- Y_UNIT_TEST(GroupByHugeCubeDeniedPragme) {
- NYql::TAstParseResult res = SqlToYql("PRAGMA GroupByCubeLimit = '4'; SELECT key FROM plato.Input GROUP BY CUBE (key, subkey, value, key + subkey as sum, key - subkey as sub);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:132: Error: GROUP BY CUBE is allowed only for 4 columns, but you use 5\n");
- }
-
- Y_UNIT_TEST(GroupByFewBigCubes) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY CUBE(key, subkey, key + subkey as sum), CUBE(value, value + key + subkey as total);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Unable to GROUP BY more than 64 groups, you try use 80 groups\n");
- }
-
- Y_UNIT_TEST(GroupByFewBigCubesWithPragmaLimit) {
- NYql::TAstParseResult res = SqlToYql("PRAGMA GroupByLimit = '16'; SELECT key FROM plato.Input GROUP BY GROUPING SETS(key, subkey, key + subkey as sum), ROLLUP(value, value + key + subkey as total);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:29: Error: Unable to GROUP BY more than 16 groups, you try use 18 groups\n");
- }
-
- Y_UNIT_TEST(NoGroupingColumn0) {
- NYql::TAstParseResult res = SqlToYql(
- "select count(1), key_first, val_first, grouping(key_first, val_first, nomind) as group\n"
- "from plato.Input group by grouping sets (cast(key as uint32) /100 as key_first, Substring(value, 1, 1) as val_first);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:71: Error: Column 'nomind' is not a grouping column\n");
- }
-
- Y_UNIT_TEST(NoGroupingColumn1) {
- NYql::TAstParseResult res = SqlToYql("select count(1), grouping(key, value) as group_duo from plato.Input group by cube (key, subkey);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:32: Error: Column 'value' is not a grouping column\n");
- }
-
- Y_UNIT_TEST(EmptyAccess0) {
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output (list0, list1) values (AsList(0, 1, 2), AsList(``));");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:73: Error: Column reference \"\" is not allowed in current scope\n");
- }
-
- Y_UNIT_TEST(EmptyAccess1) {
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output (list0, list1) values (AsList(0, 1, 2), ``);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:66: Error: Column reference \"\" is not allowed in current scope\n");
- }
-
- Y_UNIT_TEST(UseUnknownColumnInInsert) {
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output (list0, list1) values (AsList(0, 1, 2), AsList(`test`));");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:73: Error: Column reference \"test\" is not allowed in current scope\n");
- }
-
- Y_UNIT_TEST(GroupByEmptyColumn) {
- NYql::TAstParseResult res = SqlToYql("select count(1) from plato.Input group by ``;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:43: Error: Column name can not be empty\n");
- }
-
- Y_UNIT_TEST(ConvertNumberOutOfBase) {
- NYql::TAstParseResult res = SqlToYql("select 0o80l;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to parse number from string: 0o80l, char: '8' is out of base: 8\n");
- }
-
- Y_UNIT_TEST(ConvertNumberOutOfRangeForInt64ButFitsInUint64) {
- NYql::TAstParseResult res = SqlToYql("select 0xc000000000000000l;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to parse 13835058055282163712 as integer literal of Int64 type: value out of range for Int64\n");
- }
-
- Y_UNIT_TEST(ConvertNumberOutOfRangeUint64) {
- NYql::TAstParseResult res = SqlToYql("select 0xc0000000000000000l;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to parse number from string: 0xc0000000000000000l, number limit overflow\n");
-
- res = SqlToYql("select 1234234543563435151456;\n");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to parse number from string: 1234234543563435151456, number limit overflow\n");
- }
-
- Y_UNIT_TEST(ConvertNumberNegativeOutOfRange) {
- NYql::TAstParseResult res = SqlToYql("select -9223372036854775808;\n"
- "select -9223372036854775809;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:8: Error: Failed to parse negative integer: -9223372036854775809, number limit overflow\n");
- }
-
- Y_UNIT_TEST(InvaildUsageReal0) {
- NYql::TAstParseResult res = SqlToYql("select .0;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:7: Error: Unexpected token '.' : cannot match to any predicted input...\n\n");
- }
-
- Y_UNIT_TEST(InvaildUsageReal1) {
- NYql::TAstParseResult res = SqlToYql("select .0f;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:7: Error: Unexpected token '.' : cannot match to any predicted input...\n\n");
- }
-
- Y_UNIT_TEST(InvaildUsageWinFunctionWithoutWindow) {
- NYql::TAstParseResult res = SqlToYql("select lead(key, 2) from plato.Input;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to use window function Lead without window specification\n");
- }
-
- Y_UNIT_TEST(DropTableWithIfExists) {
- NYql::TAstParseResult res = SqlToYql("DROP TABLE IF EXISTS plato.foo;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop_if_exists"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(TooManyErrors) {
- const char* q = R"(
- USE plato;
- select A, B, C, D, E, F, G, H, I, J, K, L, M, N from (select b from `abc`);
-)";
-
- NYql::TAstParseResult res = SqlToYql(q, 10);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- R"(<main>:3:16: Error: Column A is not in source column set. Did you mean b?
-<main>:3:19: Error: Column B is not in source column set. Did you mean b?
-<main>:3:22: Error: Column C is not in source column set. Did you mean b?
-<main>:3:25: Error: Column D is not in source column set. Did you mean b?
-<main>:3:28: Error: Column E is not in source column set. Did you mean b?
-<main>:3:31: Error: Column F is not in source column set. Did you mean b?
-<main>:3:34: Error: Column G is not in source column set. Did you mean b?
-<main>:3:37: Error: Column H is not in source column set. Did you mean b?
-<main>:3:40: Error: Column I is not in source column set. Did you mean b?
-<main>: Error: Too many issues, code: 1
-)");
- };
-
- Y_UNIT_TEST(ShouldCloneBindingForNamedParameter) {
- NYql::TAstParseResult res = SqlToYql(R"($f = () -> {
- $value_type = TypeOf(1);
- $pair_type = StructType(
- TypeOf("2") AS key,
- $value_type AS value
- );
-
- RETURN TupleType(
- ListType($value_type),
- $pair_type);
-};
-
-select FormatType($f());
-)");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(BlockedInvalidFrameBounds) {
- auto check = [](const TString& frame, const TString& err) {
- const TString prefix = "SELECT SUM(x) OVER w FROM plato.Input WINDOW w AS (PARTITION BY key ORDER BY subkey\n";
- NYql::TAstParseResult res = SqlToYql(prefix + frame + ")");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), err);
- };
-
- check("ROWS UNBOUNDED FOLLOWING", "<main>:2:5: Error: Frame cannot start from UNBOUNDED FOLLOWING\n");
- check("ROWS BETWEEN 5 PRECEDING AND UNBOUNDED PRECEDING", "<main>:2:29: Error: Frame cannot end with UNBOUNDED PRECEDING\n");
- check("ROWS BETWEEN CURRENT ROW AND 5 PRECEDING", "<main>:2:13: Error: Frame cannot start from CURRENT ROW and end with PRECEDING\n");
- check("ROWS BETWEEN 5 FOLLOWING AND CURRENT ROW", "<main>:2:14: Error: Frame cannot start from FOLLOWING and end with CURRENT ROW\n");
- check("ROWS BETWEEN 5 FOLLOWING AND 5 PRECEDING", "<main>:2:14: Error: Frame cannot start from FOLLOWING and end with PRECEDING\n");
- }
-
- Y_UNIT_TEST(BlockedRangeValueWithoutSingleOrderBy) {
- UNIT_ASSERT(SqlToYql("SELECT COUNT(*) OVER (RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM plato.Input").IsOk());
- UNIT_ASSERT(SqlToYql("SELECT COUNT(*) OVER (RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM plato.Input").IsOk());
-
- auto res = SqlToYql("SELECT COUNT(*) OVER (RANGE 5 PRECEDING) FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:29: Error: RANGE with <offset> PRECEDING/FOLLOWING requires exactly one expression in ORDER BY partition clause\n");
-
- res = SqlToYql("SELECT COUNT(*) OVER (ORDER BY key, value RANGE 5 PRECEDING) FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Error: RANGE with <offset> PRECEDING/FOLLOWING requires exactly one expression in ORDER BY partition clause\n");
- }
-
- Y_UNIT_TEST(NoColumnsInFrameBounds) {
- NYql::TAstParseResult res = SqlToYql(
- "SELECT SUM(x) OVER w FROM plato.Input WINDOW w AS (ROWS BETWEEN\n"
- " 1 + key PRECEDING AND 2 + key FOLLOWING);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:6: Error: Column reference \"key\" is not allowed in current scope\n");
- }
-
- Y_UNIT_TEST(WarnOnEmptyFrameBounds) {
- NYql::TAstParseResult res = SqlToYql(
- "SELECT SUM(x) OVER w FROM plato.Input WINDOW w AS (PARTITION BY key ORDER BY subkey\n"
- "ROWS BETWEEN 10 FOLLOWING AND 5 FOLLOWING)");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:14: Warning: Used frame specification implies empty window frame, code: 4520\n");
- }
-
- Y_UNIT_TEST(WarnOnRankWithUnorderedWindow) {
- NYql::TAstParseResult res = SqlToYql("SELECT RANK() OVER w FROM plato.Input WINDOW w AS ()");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Warning: Rank() is used with unordered window - all rows will be considered equal to each other, code: 4521\n");
- }
-
- Y_UNIT_TEST(WarnOnRankExprWithUnorderedWindow) {
- NYql::TAstParseResult res = SqlToYql("SELECT RANK(key) OVER w FROM plato.Input WINDOW w AS ()");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Warning: Rank(<expression>) is used with unordered window - the result is likely to be undefined, code: 4521\n");
- }
-
- Y_UNIT_TEST(AnyAsTableName) {
- NYql::TAstParseResult res = SqlToYql("use plato; select * from any;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:28: Error: Unexpected token ';' : syntax error...\n\n");
- }
-
- Y_UNIT_TEST(IncorrectOrderOfLambdaOptionalArgs) {
- NYql::TAstParseResult res = SqlToYql("$f = ($x?, $y)->($x + $y); select $f(1);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Non-optional argument can not follow optional one\n");
- }
-
- Y_UNIT_TEST(IncorrectOrderOfActionOptionalArgs) {
- NYql::TAstParseResult res = SqlToYql("define action $f($x?, $y) as select $x,$y; end define; do $f(1);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:23: Error: Non-optional argument can not follow optional one\n");
- }
-
- Y_UNIT_TEST(NotAllowedQuestionOnNamedNode) {
- NYql::TAstParseResult res = SqlToYql("$f = 1; select $f?;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:18: Error: Unexpected token '?' at the end of expression\n");
- }
-
- Y_UNIT_TEST(AnyAndCrossJoin) {
- NYql::TAstParseResult res = SqlToYql("use plato; select * from any Input1 cross join Input2");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:26: Error: ANY should not be used with Cross JOIN\n");
-
- res = SqlToYql("use plato; select * from Input1 cross join any Input2");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:44: Error: ANY should not be used with Cross JOIN\n");
- }
-
- Y_UNIT_TEST(AnyWithCartesianProduct) {
- NYql::TAstParseResult res = SqlToYql("pragma AnsiImplicitCrossJoin; use plato; select * from any Input1, Input2");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:56: Error: ANY should not be used with Cross JOIN\n");
-
- res = SqlToYql("pragma AnsiImplicitCrossJoin; use plato; select * from Input1, any Input2");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:64: Error: ANY should not be used with Cross JOIN\n");
- }
-
- Y_UNIT_TEST(ErrorPlainEndAsInlineActionTerminator) {
- NYql::TAstParseResult res = SqlToYql(
- "do begin\n"
- " select 1\n"
- "; end\n");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:0: Error: Unexpected token absence : Missing DO \n\n");
- }
-
- Y_UNIT_TEST(ErrorMultiWayJoinWithUsing) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato;\n"
- "PRAGMA DisableSimpleColumns;\n"
- "SELECT *\n"
- "FROM Input1 AS a\n"
- "JOIN Input2 AS b USING(key)\n"
- "JOIN Input3 AS c ON a.key = c.key;\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:5:24: Error: Multi-way JOINs should be connected with ON clause instead of USING clause\n"
- );
- }
-
- Y_UNIT_TEST(RequireLabelInFlattenByWithDot) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input flatten by x.y");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:1:40: Error: Unnamed expression after FLATTEN BY is not allowed\n"
- );
- }
-
- Y_UNIT_TEST(WarnUnnamedColumns) {
- NYql::TAstParseResult res = SqlToYql(
- "PRAGMA WarnUnnamedColumns;\n"
- "\n"
- "SELECT key, subkey, key || subkey FROM plato.Input ORDER BY subkey;\n");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:28: Warning: Autogenerated column name column2 will be used for expression, code: 4516\n");
- }
-
- Y_UNIT_TEST(WarnSourceColumnMismatch) {
- NYql::TAstParseResult res = SqlToYql(
- "insert into plato.Output (key, subkey, new_value, one_more_value) select key as Key, subkey, value, \"x\" from plato.Input;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:51: Warning: Column names in SELECT don't match column specification in parenthesis. \"key\" doesn't match \"Key\". \"new_value\" doesn't match \"value\", code: 4517\n");
- }
-
- Y_UNIT_TEST(YtCaseInsensitive) {
- NYql::TAstParseResult res = SqlToYql("select * from PlatO.foo;");
- UNIT_ASSERT(res.Root);
-
- res = SqlToYql("use PlatO; select * from foo;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(KikimrCaseSensitive) {
- NYql::TAstParseResult res = SqlToYql("select * from PlatO.foo;", 10, "kikimr");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:15: Error: Unknown cluster: PlatO\n");
-
- res = SqlToYql("use PlatO; select * from foo;", 10, "kikimr");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:5: Error: Unknown cluster: PlatO\n");
- }
-
- Y_UNIT_TEST(DiscoveryModeForbidden) {
- NYql::TAstParseResult res = SqlToYqlWithMode("insert into plato.Output select * from plato.range(\"\", Input1, Input4)", NSQLTranslation::ESqlMode::DISCOVERY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: range is not allowed in Discovery mode, code: 4600\n");
-
- res = SqlToYqlWithMode("insert into plato.Output select * from plato.like(\"\", \"Input%\")", NSQLTranslation::ESqlMode::DISCOVERY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: like is not allowed in Discovery mode, code: 4600\n");
-
- res = SqlToYqlWithMode("insert into plato.Output select * from plato.regexp(\"\", \"Input.\")", NSQLTranslation::ESqlMode::DISCOVERY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: regexp is not allowed in Discovery mode, code: 4600\n");
-
- res = SqlToYqlWithMode("insert into plato.Output select * from plato.filter(\"\", ($name) -> { return find($name, \"Input\") is not null; })", NSQLTranslation::ESqlMode::DISCOVERY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: filter is not allowed in Discovery mode, code: 4600\n");
-
- res = SqlToYqlWithMode("select Path from plato.folder(\"\") where Type == \"table\"", NSQLTranslation::ESqlMode::DISCOVERY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:18: Error: folder is not allowed in Discovery mode, code: 4600\n");
- }
-
- Y_UNIT_TEST(YsonFuncWithoutArgs) {
- UNIT_ASSERT(SqlToYql("SELECT Yson::SerializeText(Yson::From());").IsOk());
- }
-
- Y_UNIT_TEST(CanNotUseOrderByInNonLastSelectInUnionAllChain) {
- auto req = "pragma AnsiOrderByLimitInUnionAll;\n"
- "use plato;\n"
- "\n"
- "select * from Input order by key\n"
- "union all\n"
- "select * from Input order by key limit 1;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:21: Error: ORDER BY within UNION ALL is only allowed after last subquery\n");
- }
-
- Y_UNIT_TEST(CanNotUseLimitInNonLastSelectInUnionAllChain) {
- auto req = "pragma AnsiOrderByLimitInUnionAll;\n"
- "use plato;\n"
- "\n"
- "select * from Input limit 1\n"
- "union all\n"
- "select * from Input order by key limit 1;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:21: Error: LIMIT within UNION ALL is only allowed after last subquery\n");
- }
-
- Y_UNIT_TEST(CanNotUseDiscardInNonFirstSelectInUnionAllChain) {
- auto req = "pragma AnsiOrderByLimitInUnionAll;\n"
- "use plato;\n"
- "\n"
- "select * from Input\n"
- "union all\n"
- "discard select * from Input;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:6:1: Error: DISCARD within UNION ALL is only allowed before first subquery\n");
- }
-
- Y_UNIT_TEST(CanNotUseIntoResultInNonLastSelectInUnionAllChain) {
- auto req = "use plato;\n"
- "pragma AnsiOrderByLimitInUnionAll;\n"
- "\n"
- "select * from Input\n"
- "union all\n"
- "discard select * from Input;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:6:1: Error: DISCARD within UNION ALL is only allowed before first subquery\n");
- }
-
- Y_UNIT_TEST(YsonStrictInvalidPragma) {
- auto res = SqlToYql("pragma yson.Strict = \"wrong\";");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:22: Error: Expected 'true', 'false' or no parameter for: Strict\n");
- }
-
- Y_UNIT_TEST(WarnTableNameInSomeContexts) {
- UNIT_ASSERT(SqlToYql("use plato; select TableName() from Input;").IsOk());
- UNIT_ASSERT(SqlToYql("use plato; select TableName(\"aaaa\");").IsOk());
- UNIT_ASSERT(SqlToYql("select TableName(\"aaaa\", \"yt\");").IsOk());
-
- auto res = SqlToYql("select TableName() from plato.Input;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: TableName requires either service name as second argument or current cluster name\n");
-
- res = SqlToYql("use plato;\n"
- "select TableName() from Input1 as a join Input2 as b using(key);");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:8: Warning: TableName() may produce empty result when used in ambiguous context (with JOIN), code: 4525\n");
-
- res = SqlToYql("use plato;\n"
- "select SOME(TableName()), key from Input group by key;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:13: Warning: TableName() will produce empty result when used with aggregation.\n"
- "Please consult documentation for possible workaround, code: 4525\n");
- }
-
- Y_UNIT_TEST(WarnOnDistincWithHavingWithoutAggregations) {
- auto res = SqlToYql("select distinct key from plato.Input having key != '0';");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Warning: The usage of HAVING without aggregations with SELECT DISTINCT is non-standard and will stop working soon. Please use WHERE instead., code: 4526\n");
- }
-
- Y_UNIT_TEST(FlattenByExprWithNestedNull) {
- auto res = SqlToYql("USE plato;\n"
- "\n"
- "SELECT * FROM (SELECT 1 AS region_id)\n"
- "FLATTEN BY (\n"
- " CAST($unknown(region_id) AS List<String>) AS region\n"
- ")");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:10: Error: Unknown name: $unknown\n");
- }
-
- Y_UNIT_TEST(EmptySymbolNameIsForbidden) {
- auto req = " $`` = 1; select $``;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:5: Error: Empty symbol name is not allowed\n");
- }
-
- Y_UNIT_TEST(WarnOnBinaryOpWithNullArg) {
- auto req = "select * from plato.Input where cast(key as Int32) != NULL";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Warning: Binary operation != will return NULL here, code: 4529\n");
-
- req = "select 1 or null";
- res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "");
- }
-
- Y_UNIT_TEST(ErrorIfTableSampleArgUsesColumns) {
- auto req = "SELECT key FROM plato.Input TABLESAMPLE BERNOULLI(MIN_OF(100.0, CAST(subkey as Int32)));";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:70: Error: Column reference \"subkey\" is not allowed in current scope\n");
- }
-
- Y_UNIT_TEST(DerivedColumnListForSelectIsNotSupportedYet) {
- auto req = "SELECT a,b,c FROM plato.Input as t(x,y,z);";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:35: Error: Derived column list is only supported for VALUES\n");
- }
-
- Y_UNIT_TEST(ErrorIfValuesHasDifferentCountOfColumns) {
- auto req = "VALUES (1,2,3), (4,5);";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: All VALUES items should have same size: expecting 3, got 2\n");
- }
-
- Y_UNIT_TEST(ErrorIfDerivedColumnSizeExceedValuesColumnCount) {
- auto req = "SELECT * FROM(VALUES (1,2), (3,4)) as t(x,y,z);";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: Derived column list size exceeds column count in VALUES\n");
- }
-
- Y_UNIT_TEST(WarnoOnAutogeneratedNamesForValues) {
- auto req = "PRAGMA WarnUnnamedColumns;\n"
- "SELECT * FROM (VALUES (1,2,3,4), (5,6,7,8)) as t(x,y);";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:16: Warning: Autogenerated column names column2...column3 will be used here, code: 4516\n");
- }
-
- Y_UNIT_TEST(ErrUnionAllWithOrderByWithoutExplicitLegacyMode) {
- auto req = "use plato;\n"
- "\n"
- "select * from Input order by key\n"
- "union all\n"
- "select * from Input order by key;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:21: Error: ORDER BY within UNION ALL is only allowed after last subquery\n");
- }
-
- Y_UNIT_TEST(ErrUnionAllWithLimitWithoutExplicitLegacyMode) {
- auto req = "use plato;\n"
- "\n"
- "select * from Input limit 10\n"
- "union all\n"
- "select * from Input limit 1;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:21: Error: LIMIT within UNION ALL is only allowed after last subquery\n");
- }
-
- Y_UNIT_TEST(ErrUnionAllWithIntoResultWithoutExplicitLegacyMode) {
- auto req = "use plato;\n"
- "\n"
- "select * from Input into result aaa\n"
- "union all\n"
- "select * from Input;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:21: Error: INTO RESULT within UNION ALL is only allowed after last subquery\n");
- }
-
- Y_UNIT_TEST(ErrUnionAllWithDiscardWithoutExplicitLegacyMode) {
- auto req = "use plato;\n"
- "\n"
- "select * from Input\n"
- "union all\n"
- "discard select * from Input;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:1: Error: DISCARD within UNION ALL is only allowed before first subquery\n");
- }
-
- Y_UNIT_TEST(ErrUnionAllKeepsIgnoredOrderByWarning) {
- auto req = "use plato;\n"
- "\n"
- "SELECT * FROM (\n"
- " SELECT * FROM Input\n"
- " UNION ALL\n"
- " SELECT t.* FROM Input AS t ORDER BY t.key\n"
- ");";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:3: Warning: ORDER BY without LIMIT in subquery will be ignored, code: 4504\n"
- "<main>:6:39: Error: Unknown correlation name: t\n");
- }
-
- Y_UNIT_TEST(ErrOrderByIgnoredButCheckedForMissingColumns) {
- auto req = "$src = SELECT key FROM (SELECT 1 as key, 2 as subkey) ORDER BY x; SELECT * FROM $src;";
- ExpectFailWithError(req, "<main>:1:8: Warning: ORDER BY without LIMIT in subquery will be ignored, code: 4504\n"
- "<main>:1:64: Error: Column x is not in source column set\n");
-
- req = "$src = SELECT key FROM plato.Input ORDER BY x; SELECT * FROM $src;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Warning: ORDER BY without LIMIT in subquery will be ignored, code: 4504\n");
- }
-
- Y_UNIT_TEST(InvalidTtlInterval) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (Key Uint32, CreatedAt Timestamp, PRIMARY KEY (Key))
- WITH (TTL = 1 On CreatedAt);
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:25: Error: Literal of Interval type is expected for TTL\n"
- "<main>:4:25: Error: Invalid TTL settings\n");
- }
-
- Y_UNIT_TEST(InvalidTtlUnit) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (Key Uint32, CreatedAt Uint32, PRIMARY KEY (Key))
- WITH (TTL = Interval("P1D") On CreatedAt AS PICOSECONDS);
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "<main>:4:56: Error: Unexpected token 'PICOSECONDS'");
- }
-
- Y_UNIT_TEST(InvalidChangefeedSink) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (SINK_TYPE = "S3", MODE = "KEYS_ONLY", FORMAT = "json")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:55: Error: Unknown changefeed sink type: S3\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedSettings) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (SINK_TYPE = "local", FOO = "bar")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:64: Error: Unknown changefeed setting: FOO\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedInitialScan) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", INITIAL_SCAN = "foo")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:95: Error: Literal of Bool type is expected for INITIAL_SCAN\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedVirtualTimestamps) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", VIRTUAL_TIMESTAMPS = "foo")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:101: Error: Literal of Bool type is expected for VIRTUAL_TIMESTAMPS\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedResolvedTimestamps) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", BARRIERS_INTERVAL = "foo")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:100: Error: Literal of Interval type is expected for BARRIERS_INTERVAL\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedRetentionPeriod) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", RETENTION_PERIOD = "foo")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:99: Error: Literal of Interval type is expected for RETENTION_PERIOD\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedTopicPartitions) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", TOPIC_MIN_ACTIVE_PARTITIONS = "foo")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:110: Error: Literal of integer type is expected for TOPIC_MIN_ACTIVE_PARTITIONS\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedAwsRegion) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", AWS_REGION = true)
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:93: Error: Literal of String type is expected for AWS_REGION\n");
- }
-
- Y_UNIT_TEST(ErrJoinWithGroupingSetsWithoutCorrelationName) {
- auto req = "USE plato;\n"
- "\n"
- "SELECT k1, k2, subkey\n"
- "FROM T1 AS a JOIN T2 AS b USING (key)\n"
- "GROUP BY GROUPING SETS(\n"
- " (a.key as k1, b.subkey as k2),\n"
- " (k1),\n"
- " (subkey)\n"
- ");";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:8:4: Error: Columns in grouping sets should have correlation name, error in key: subkey\n");
- }
-
- Y_UNIT_TEST(ErrJoinWithGroupByWithoutCorrelationName) {
- auto req = "USE plato;\n"
- "\n"
- "SELECT k1, k2,\n"
- " value\n"
- "FROM T1 AS a JOIN T2 AS b USING (key)\n"
- "GROUP BY a.key as k1, b.subkey as k2,\n"
- " value;";
- ExpectFailWithError(req,
- "<main>:7:5: Error: Columns in GROUP BY should have correlation name, error in key: value\n");
- }
-
- Y_UNIT_TEST(ErrWithMissingFrom) {
- auto req = "select 1 as key where 1 > 1;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:25: Error: Filtering is not allowed without FROM\n");
-
- req = "select 1 + count(*);";
- res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Aggregation is not allowed without FROM\n");
-
- req = "select 1 as key, subkey + value;";
- res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:18: Error: Column reference 'subkey'\n"
- "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:27: Error: Column reference 'value'\n");
-
- req = "select count(1) group by key;";
- res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:26: Error: Column reference 'key'\n");
- }
-
- Y_UNIT_TEST(ErrWithMissingFromForWindow) {
- auto req = "$c = () -> (1 + count(1) over w);\n"
- "select $c();";
- ExpectFailWithError(req,
- "<main>:1:9: Error: Window and aggregation functions are not allowed in this context\n"
- "<main>:1:17: Error: Failed to use aggregation function Count without window specification or in wrong place\n");
-
- req = "$c = () -> (1 + lead(1) over w);\n"
- "select $c();";
- ExpectFailWithError(req,
- "<main>:1:17: Error: Window functions are not allowed in this context\n"
- "<main>:1:17: Error: Failed to use window function Lead without window specification or in wrong place\n");
-
- req = "select 1 + count(1) over w window w as ();";
- ExpectFailWithError(req,
- "<main>:1:1: Error: Window and aggregation functions are not allowed without FROM\n"
- "<main>:1:12: Error: Failed to use aggregation function Count without window specification or in wrong place\n");
-
- req = "select 1 + lead(1) over w window w as ();";
- ExpectFailWithError(req,
- "<main>:1:12: Error: Window functions are not allowed without FROM\n"
- "<main>:1:12: Error: Failed to use window function Lead without window specification or in wrong place\n");
- }
-
- Y_UNIT_TEST(ErrWithMissingFromForInplaceWindow) {
- auto req = "$c = () -> (1 + count(1) over ());\n"
- "select $c();";
- ExpectFailWithError(req,
- "<main>:1:26: Error: Window and aggregation functions are not allowed in this context\n");
-
- req = "$c = () -> (1 + lead(1) over (rows between unbounded preceding and current row));\n"
- "select $c();";
- ExpectFailWithError(req,
- "<main>:1:25: Error: Window and aggregation functions are not allowed in this context\n");
-
- req = "select 1 + count(1) over ();";
- ExpectFailWithError(req,
- "<main>:1:1: Error: Window and aggregation functions are not allowed without FROM\n"
- "<main>:1:12: Error: Failed to use aggregation function Count without window specification or in wrong place\n");
-
- req = "select 1 + lead(1) over (rows between current row and unbounded following);";
- ExpectFailWithError(req,
- "<main>:1:12: Error: Window functions are not allowed without FROM\n"
- "<main>:1:12: Error: Failed to use window function Lead without window specification or in wrong place\n");
- }
-
- Y_UNIT_TEST(ErrDistinctInWrongPlace) {
- auto req = "select Some::Udf(distinct key) from plato.Input;";
- ExpectFailWithError(req,
- "<main>:1:18: Error: DISTINCT can only be used in aggregation functions\n");
- req = "select sum(key)(distinct foo) from plato.Input;";
- ExpectFailWithError(req,
- "<main>:1:17: Error: DISTINCT can only be used in aggregation functions\n");
-
- req = "select len(distinct foo) from plato.Input;";
- ExpectFailWithError(req,
- "<main>:1:8: Error: DISTINCT can only be used in aggregation functions\n");
-
- req = "$foo = ($x) -> ($x); select $foo(distinct key) from plato.Input;";
- ExpectFailWithError(req,
- "<main>:1:34: Error: DISTINCT can only be used in aggregation functions\n");
- }
-
- Y_UNIT_TEST(ErrForNotSingleChildInInlineAST) {
- ExpectFailWithError("select YQL::\"\"",
- "<main>:1:8: Error: Failed to parse YQL: expecting AST root node with single child, but got 0\n");
- ExpectFailWithError("select YQL::@@ \t@@",
- "<main>:1:8: Error: Failed to parse YQL: expecting AST root node with single child, but got 0\n");
- auto req = "$lambda = YQL::@@(lambda '(x)(+ x x)) (lambda '(y)(+ y y))@@;\n"
- "select ListMap([1, 2, 3], $lambda);";
- ExpectFailWithError(req,
- "<main>:1:11: Error: Failed to parse YQL: expecting AST root node with single child, but got 2\n");
- }
-
- Y_UNIT_TEST(ErrEmptyColumnName) {
- ExpectFailWithError("select * without \"\" from plato.Input",
- "<main>:1:18: Error: String literal can not be used here\n");
-
- ExpectFailWithError("select * without `` from plato.Input;",
- "<main>:1:18: Error: Empty column name is not allowed\n");
-
- ExpectFailWithErrorForAnsiLexer("select * without \"\" from plato.Input",
- "<main>:1:18: Error: Empty column name is not allowed\n");
-
- ExpectFailWithErrorForAnsiLexer("select * without `` from plato.Input;",
- "<main>:1:18: Error: Empty column name is not allowed\n");
- }
-
- Y_UNIT_TEST(ErrOnNonZeroArgumentsForTableRows) {
- ExpectFailWithError("$udf=\"\";process plato.Input using $udf(TableRows(k))",
- "<main>:1:40: Error: TableRows requires exactly 0 arguments\n");
- }
-
- Y_UNIT_TEST(ErrGroupByWithAggregationFunctionAndDistinctExpr) {
- ExpectFailWithError("select * from plato.Input group by count(distinct key|key)",
- "<main>:1:36: Error: Unable to GROUP BY aggregated values\n");
- }
-
- // FIXME: check if we can get old behaviour
-#if 0
- Y_UNIT_TEST(ErrWithSchemaWithColumnsWithoutType) {
- ExpectFailWithError("select * from plato.Input with COLUMNs",
- "<main>:1:32: Error: Expected type after COLUMNS\n"
- "<main>:1:32: Error: Failed to parse table hints\n");
-
- ExpectFailWithError("select * from plato.Input with scheMa",
- "<main>:1:32: Error: Expected type after SCHEMA\n"
- "<main>:1:32: Error: Failed to parse table hints\n");
- }
-#endif
-
- Y_UNIT_TEST(ErrCollectPreaggregatedInListLiteralWithoutFrom) {
- ExpectFailWithError("SELECT([VARIANCE(DISTINCT[])])",
- "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:9: Error: Column reference '_yql_preagg_Variance0'\n");
- }
-
- Y_UNIT_TEST(ErrGroupBySmartParenAsTuple) {
- ExpectFailWithError("SELECT * FROM plato.Input GROUP BY (k, v,)",
- "<main>:1:41: Error: Unexpected trailing comma in grouping elements list\n");
- }
-
- Y_UNIT_TEST(HandleNestedSmartParensInGroupBy) {
- ExpectFailWithError("SELECT * FROM plato.Input GROUP BY (+() as k)",
- "<main>:1:37: Error: Unable to GROUP BY constant expression\n");
- }
-
- Y_UNIT_TEST(ErrRenameWithAddColumn) {
- ExpectFailWithError("USE plato; ALTER TABLE table RENAME TO moved, ADD COLUMN addc uint64",
- "<main>:1:40: Error: RENAME TO can not be used together with another table action\n");
- }
-
- Y_UNIT_TEST(ErrAddColumnAndRename) {
- // FIXME: fix positions in ALTER TABLE
- ExpectFailWithError("USE plato; ALTER TABLE table ADD COLUMN addc uint64, RENAME TO moved",
- "<main>:1:46: Error: RENAME TO can not be used together with another table action\n");
- }
-
- Y_UNIT_TEST(InvalidUuidValue) {
- ExpectFailWithError("SELECT Uuid('123e4567ae89ba12d3aa456a426614174ab0')",
- "<main>:1:8: Error: Invalid value \"123e4567ae89ba12d3aa456a426614174ab0\" for type Uuid\n");
- ExpectFailWithError("SELECT Uuid('123e4567ae89b-12d3-a456-426614174000')",
- "<main>:1:8: Error: Invalid value \"123e4567ae89b-12d3-a456-426614174000\" for type Uuid\n");
- }
-
- Y_UNIT_TEST(WindowFunctionWithoutOver) {
- ExpectFailWithError("SELECT LAST_VALUE(foo) FROM plato.Input",
- "<main>:1:8: Error: Can't use window function LastValue without window specification (OVER keyword is missing)\n");
- ExpectFailWithError("SELECT LAST_VALUE(foo) FROM plato.Input GROUP BY key",
- "<main>:1:8: Error: Can't use window function LastValue without window specification (OVER keyword is missing)\n");
- }
-
- Y_UNIT_TEST(CreateAlterUserWithLoginNoLogin) {
- auto reqCreateUser = SqlToYql(R"(
- USE plato;
- CREATE USER user1;
- )");
-
- UNIT_ASSERT(reqCreateUser.IsOk());
-
- auto reqAlterUser = SqlToYql(R"(
- USE plato;
- ALTER USER user1;
- )");
-
- UNIT_ASSERT(!reqAlterUser.IsOk());
- UNIT_ASSERT_STRING_CONTAINS(reqAlterUser.Issues.ToString(), "Error: Unexpected token ';' : cannot match to any predicted input...");
-
- auto reqPasswordAndLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 PASSWORD '123' LOGIN;
- )");
-
- UNIT_ASSERT(reqPasswordAndLogin.IsOk());
-
- auto reqPasswordAndNoLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 PASSWORD '123' NOLOGIN;
- )");
-
- UNIT_ASSERT(reqPasswordAndNoLogin.IsOk());
-
- auto reqLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 LOGIN;
- )");
-
- UNIT_ASSERT(reqLogin.IsOk());
-
- auto reqNoLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 NOLOGIN;
- )");
-
- UNIT_ASSERT(reqNoLogin.IsOk());
-
- auto reqLoginNoLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 LOGIN NOLOGIN;
- )");
-
- UNIT_ASSERT(!reqLoginNoLogin.IsOk());
- UNIT_ASSERT_STRING_CONTAINS(reqLoginNoLogin.Issues.ToString(), "Error: Conflicting or redundant options");
-
- auto reqAlterLoginNoLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 LOGIN;
- ALTER USER user1 NOLOGIN;
- )");
-
- UNIT_ASSERT(reqAlterLoginNoLogin.IsOk());
-
- auto reqAlterLoginNoLoginWithPassword = SqlToYql(R"(
- USE plato;
- CREATE USER user1 LOGIN;
- ALTER USER user1 PASSWORD '321' NOLOGIN;
- )");
-
- UNIT_ASSERT(reqAlterLoginNoLoginWithPassword.IsOk());
- }
-
- Y_UNIT_TEST(CreateUserWithHash) {
- auto reqCreateUser = SqlToYql(R"(
- USE plato;
- CREATE USER user1 HASH '{
- "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
- "salt": "U+tzBtgo06EBQCjlARA6Jg==",
- "type": "argon2id"
- }';
- )");
-
- UNIT_ASSERT(reqCreateUser.IsOk());
-
- auto reqCreateUserWithNoLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 HASH '{
- "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
- "salt": "U+tzBtgo06EBQCjlARA6Jg==",
- "type": "argon2id"
- }'
- NOLOGIN;
- )");
-
- UNIT_ASSERT(reqCreateUserWithNoLogin.IsOk());
-
- auto reqCreateUserWithPassword = SqlToYql(R"(
- USE plato;
- CREATE USER user1 HASH '{
- "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
- "salt": "U+tzBtgo06EBQCjlARA6Jg==",
- "type": "argon2id"
- }'
- PASSWORD '123';
- )");
-
- UNIT_ASSERT(!reqCreateUserWithPassword.IsOk());
- UNIT_ASSERT_STRING_CONTAINS(reqCreateUserWithPassword.Issues.ToString(), "Error: Conflicting or redundant options");
-
- auto reqAlterUser = SqlToYql(R"(
- USE plato;
- CREATE USER user1;
- ALTER USER user1 HASH '{
- "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
- "salt": "U+tzBtgo06EBQCjlARA6Jg==",
- "type": "argon2id"
- }';
- )");
-
- UNIT_ASSERT(reqAlterUser.IsOk());
- }
-
- Y_UNIT_TEST(CreateAlterUserWithoutCluster) {
- ExpectFailWithError("\n CREATE USER user ENCRYPTED PASSWORD 'foobar';", "<main>:2:2: Error: USE statement is missing - no default cluster is selected\n");
- ExpectFailWithError("ALTER USER CURRENT_USER RENAME TO $foo;", "<main>:1:1: Error: USE statement is missing - no default cluster is selected\n");
- }
-
- Y_UNIT_TEST(ModifyPermissionsWithoutCluster) {
- ExpectFailWithError("\n GRANT CONNECT ON `/Root` TO user;", "<main>:2:2: Error: USE statement is missing - no default cluster is selected\n");
- ExpectFailWithError("\n REVOKE MANAGE ON `/Root` FROM user;", "<main>:2:2: Error: USE statement is missing - no default cluster is selected\n");
- }
-
- Y_UNIT_TEST(ReservedRoleNames) {
- ExpectFailWithError("USE plato; CREATE USER current_User;", "<main>:1:24: Error: System role CURRENT_USER can not be used here\n");
- ExpectFailWithError("USE plato; ALTER USER current_User RENAME TO Current_role", "<main>:1:46: Error: System role CURRENT_ROLE can not be used here\n");
- UNIT_ASSERT(SqlToYql("USE plato; DROP GROUP IF EXISTS a, b, c, current_User;").IsOk());
- }
-
- Y_UNIT_TEST(DisableClassicDivisionWithError) {
- ExpectFailWithError("pragma ClassicDivision = 'false'; select $foo / 30;", "<main>:1:42: Error: Unknown name: $foo\n");
- }
-
- Y_UNIT_TEST(AggregationOfAgrregatedDistinctExpr) {
- ExpectFailWithError("select sum(sum(distinct x + 1)) from plato.Input", "<main>:1:12: Error: Aggregation of aggregated values is forbidden\n");
- }
-
- Y_UNIT_TEST(WarnForUnusedSqlHint) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input1 as a join /*+ merge() */ plato.Input2 as b using(key);\n"
- "select --+ foo(bar)\n"
- " 1;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:23: Warning: Hint foo will not be used, code: 4534\n");
- }
-
- Y_UNIT_TEST(WarnForDeprecatedSchema) {
- NSQLTranslation::TTranslationSettings settings;
- settings.ClusterMapping["s3bucket"] = NYql::S3ProviderName;
- NYql::TAstParseResult res = SqlToYqlWithSettings("select * from s3bucket.`foo` with schema (col1 Int32, String as col2, Int64 as col3);", settings);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "Warning: Deprecated syntax for positional schema: please use 'column type' instead of 'type AS column', code: 4535\n");
- }
-
- Y_UNIT_TEST(ErrorOnColumnNameInMaxByLimit) {
- ExpectFailWithError(
- "SELECT AGGREGATE_BY(AsTuple(value, key), AggregationFactory(\"MAX_BY\", subkey)) FROM plato.Input;",
- "<main>:1:42: Error: Source does not allow column references\n"
- "<main>:1:71: Error: Column reference 'subkey'\n");
- }
-
- Y_UNIT_TEST(ErrorInLibraryWithTopLevelNamedSubquery) {
- TString withUnusedSubq = "$unused = select max(key) from plato.Input;\n"
- "\n"
- "define subquery $foo() as\n"
- " $count = select count(*) from plato.Input;\n"
- " select * from plato.Input limit $count / 2;\n"
- "end define;\n"
- "export $foo;\n";
- UNIT_ASSERT(SqlToYqlWithMode(withUnusedSubq, NSQLTranslation::ESqlMode::LIBRARY).IsOk());
-
- TString withTopLevelSubq = "$count = select count(*) from plato.Input;\n"
- "\n"
- "define subquery $foo() as\n"
- " select * from plato.Input limit $count / 2;\n"
- "end define;\n"
- "export $foo;\n";
- auto res = SqlToYqlWithMode(withTopLevelSubq, NSQLTranslation::ESqlMode::LIBRARY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: Named subquery can not be used as a top level statement in libraries\n");
- }
-
- Y_UNIT_TEST(SessionStartAndSessionStateShouldSurviveSessionWindowArgsError){
- TString query = R"(
- $init = ($_row) -> (min(1, 2)); -- error: aggregation func min() can not be used here
- $calculate = ($_row, $_state) -> (1);
- $update = ($_row, $_state) -> (2);
- SELECT
- SessionStart() over w as session_start,
- SessionState() over w as session_state,
- FROM plato.Input as t
- WINDOW w AS (
- PARTITION BY user, SessionWindow(ts + 1, $init, $update, $calculate)
- )
- )";
- ExpectFailWithError(query, "<main>:2:33: Error: Aggregation function Min requires exactly 1 argument(s), given: 2\n");
- }
-
- Y_UNIT_TEST(ScalarContextUsage1) {
- TString query = R"(
- $a = (select 1 as x, 2 as y);
- select 1 + $a;
- )";
- ExpectFailWithError(query, "<main>:2:39: Error: Source used in expression should contain one concrete column\n"
- "<main>:3:24: Error: Source is used here\n");
- }
-
- Y_UNIT_TEST(ScalarContextUsage2) {
- TString query = R"(
- use plato;
- $a = (select 1 as x, 2 as y);
- select * from concat($a);
- )";
- ExpectFailWithError(query, "<main>:3:39: Error: Source used in expression should contain one concrete column\n"
- "<main>:4:34: Error: Source is used here\n");
- }
-
- Y_UNIT_TEST(ScalarContextUsage3) {
- TString query = R"(
- use plato;
- $a = (select 1 as x, 2 as y);
- select * from range($a);
- )";
- ExpectFailWithError(query, "<main>:3:39: Error: Source used in expression should contain one concrete column\n"
- "<main>:4:33: Error: Source is used here\n");
- }
-
- Y_UNIT_TEST(ScalarContextUsage4) {
- TString query = R"(
- use plato;
- $a = (select 1 as x, 2 as y);
- insert into $a select 1;
- )";
- ExpectFailWithError(query, "<main>:3:39: Error: Source used in expression should contain one concrete column\n"
- "<main>:4:25: Error: Source is used here\n");
- }
-}
-
-void CheckUnused(const TString& req, const TString& symbol, unsigned row, unsigned col) {
- auto res = SqlToYql(req);
-
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), TStringBuilder() << "<main>:" << row << ":" << col << ": Warning: Symbol " << symbol << " is not used, code: 4527\n");
-}
-
-Y_UNIT_TEST_SUITE(WarnUnused) {
- Y_UNIT_TEST(ActionOrSubquery) {
- TString req = " $a()\n"
- "as select 1;\n"
- "end define;\n"
- "\n"
- "select 1;";
- CheckUnused("define action\n" + req, "$a", 2, 3);
- CheckUnused("define subquery\n" + req, "$a", 2, 3);
- }
-
- Y_UNIT_TEST(Import) {
- TString req = "import lib1 symbols\n"
- " $sqr;\n"
- "select 1;";
- CheckUnused(req, "$sqr", 2, 3);
-
- req = "import lib1 symbols\n"
- " $sqr as\n"
- " $sq;\n"
- "select 1;";
- CheckUnused(req, "$sq", 3, 5);
- }
-
- Y_UNIT_TEST(NamedNodeStatement) {
- TString req = " $a, $a = AsTuple(1, 2);\n"
- "select $a;";
- CheckUnused(req, "$a", 1, 2);
- req = "$a, $b = AsTuple(1, 2);\n"
- "select $a;";
- CheckUnused(req, "$b", 1, 6);
- CheckUnused(" $a = 1; $a = 2; select $a;", "$a", 1, 2);
- }
-
- Y_UNIT_TEST(Declare) {
- CheckUnused("declare $a as String;select 1;", "$a", 1, 9);
- }
-
- Y_UNIT_TEST(ActionParams) {
- TString req = "define action $a($x, $y) as\n"
- " select $x;\n"
- "end define;\n"
- "\n"
- "do $a(1,2);";
- CheckUnused(req, "$y", 1, 22);
- }
-
- Y_UNIT_TEST(SubqueryParams) {
- TString req = "use plato;\n"
- "define subquery $q($name, $x) as\n"
- " select * from $name;\n"
- "end define;\n"
- "\n"
- "select * from $q(\"Input\", 1);";
- CheckUnused(req, "$x", 2, 27);
- }
-
- Y_UNIT_TEST(For) {
- TString req = "define action $a() as\n"
- " select 1;\n"
- "end define;\n"
- "\n"
- "for $i in ListFromRange(1, 10)\n"
- "do $a();";
- CheckUnused(req, "$i", 5, 5);
- }
-
- Y_UNIT_TEST(LambdaParams) {
- TString req = "$lambda = ($x, $y) -> ($x);\n"
- "select $lambda(1, 2);";
- CheckUnused(req, "$y", 1, 16);
- }
-
- Y_UNIT_TEST(InsideLambdaBody) {
- TString req = "$lambda = () -> {\n"
- " $x = 1; return 1;\n"
- "};\n"
- "select $lambda();";
- CheckUnused(req, "$x", 2, 3);
- req = "$lambda = () -> {\n"
- " $x = 1; $x = 2; return $x;\n"
- "};\n"
- "select $lambda();";
- CheckUnused(req, "$x", 2, 3);
- }
-
- Y_UNIT_TEST(InsideAction) {
- TString req = "define action $a() as\n"
- " $x = 1; select 1;\n"
- "end define;\n"
- "\n"
- "do $a();";
- CheckUnused(req, "$x", 2, 3);
- req = "define action $a() as\n"
- " $x = 1; $x = 2; select $x;\n"
- "end define;\n"
- "\n"
- "do $a();";
- CheckUnused(req, "$x", 2, 3);
- }
-
- Y_UNIT_TEST(NoWarnOnNestedActions) {
- auto req = "pragma warning(\"error\", \"4527\");\n"
- "define action $action($b) as\n"
- " define action $aaa() as\n"
- " select $b;\n"
- " end define;\n"
- " do $aaa();\n"
- "end define;\n"
- "\n"
- "do $action(1);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(NoWarnForUsageAfterSubquery) {
- auto req = "use plato;\n"
- "pragma warning(\"error\", \"4527\");\n"
- "\n"
- "$a = 1;\n"
- "\n"
- "define subquery $q($table) as\n"
- " select * from $table;\n"
- "end define;\n"
- "\n"
- "select * from $q(\"Input\");\n"
- "select $a;";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-}
-
-Y_UNIT_TEST_SUITE(AnonymousNames) {
- Y_UNIT_TEST(ReferenceAnonymousVariableIsForbidden) {
- auto req = "$_ = 1; select $_;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:16: Error: Unable to reference anonymous name $_\n");
-
- req = "$`_` = 1; select $`_`;";
- res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:18: Error: Unable to reference anonymous name $_\n");
- }
-
- Y_UNIT_TEST(Declare) {
- auto req = "declare $_ as String;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:9: Error: Can not use anonymous name '$_' in DECLARE statement\n");
- }
-
- Y_UNIT_TEST(ActionSubquery) {
- auto req = "define action $_() as select 1; end define;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:15: Error: Can not use anonymous name '$_' as ACTION name\n");
-
- req = "define subquery $_() as select 1; end define;";
- res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: Can not use anonymous name '$_' as SUBQUERY name\n");
- }
-
- Y_UNIT_TEST(Import) {
- auto req = "import lib symbols $sqr as $_;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:28: Error: Can not import anonymous name $_\n");
- }
-
- Y_UNIT_TEST(Export) {
- auto req = "export $_;";
- auto res = SqlToYqlWithMode(req, NSQLTranslation::ESqlMode::LIBRARY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Can not export anonymous name $_\n");
- }
-
- Y_UNIT_TEST(AnonymousInActionArgs) {
- auto req = "pragma warning(\"error\", \"4527\");\n"
- "define action $a($_, $y, $_) as\n"
- " select $y;\n"
- "end define;\n"
- "\n"
- "do $a(1,2,3);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(AnonymousInSubqueryArgs) {
- auto req = "use plato;\n"
- "pragma warning(\"error\", \"4527\");\n"
- "define subquery $q($_, $y, $_) as\n"
- " select * from $y;\n"
- "end define;\n"
- "\n"
- "select * from $q(1,\"Input\",3);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(AnonymousInLambdaArgs) {
- auto req = "pragma warning(\"error\", \"4527\");\n"
- "$lambda = ($_, $x, $_) -> ($x);\n"
- "select $lambda(1,2,3);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(AnonymousInFor) {
- auto req = "pragma warning(\"error\", \"4527\");\n"
- "evaluate for $_ in ListFromRange(1, 10) do begin select 1; end do;";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(Assignment) {
- auto req = "pragma warning(\"error\", \"4527\");\n"
- "$_ = 1;\n"
- "$_, $x, $_ = AsTuple(1,2,3);\n"
- "select $x;";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
}
-Y_UNIT_TEST_SUITE(JsonValue) {
- Y_UNIT_TEST(JsonValueArgumentCount) {
- NYql::TAstParseResult res = SqlToYql("select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json));");
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Error: Unexpected token ')' : syntax error...\n\n");
- }
-
- Y_UNIT_TEST(JsonValueJsonPathMustBeLiteralString) {
- NYql::TAstParseResult res = SqlToYql("$jsonPath = \"strict $.key\"; select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), $jsonPath);");
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:79: Error: Unexpected token absence : Missing STRING_VALUE \n\n");
- }
-
- Y_UNIT_TEST(JsonValueTranslation) {
- NYql::TAstParseResult res = SqlToYql("select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\");");
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"strict $.key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("SafeCast"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("DataType 'Json"));
- };
-
- TWordCountHive elementStat({"JsonValue"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["JsonValue"]);
- }
-
- Y_UNIT_TEST(JsonValueReturningSection) {
- for (const auto& typeName : {"Bool", "Int64", "Double", "String"}) {
- NYql::TAstParseResult res = SqlToYql(
- TStringBuilder() << "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" RETURNING " << typeName << ");"
- );
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"strict $.key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("SafeCast"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("DataType 'Json"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(TStringBuilder() << "DataType '" << typeName));
- };
-
- TWordCountHive elementStat({typeName});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat[typeName] > 0);
- }
- }
-
- Y_UNIT_TEST(JsonValueInvalidReturningType) {
- NYql::TAstParseResult res = SqlToYql("select JSON_VALUE(CAST(@@{'key': 1238}@@ as Json), 'strict $.key' RETURNING invalid);");
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:77: Error: Unknown simple type 'invalid'\n");
- }
-
- Y_UNIT_TEST(JsonValueAndReturningInExpressions) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato\n;"
- "$json_value = \"some string\";\n"
- "SELECT $json_value;\n"
- "SELECT 1 as json_value;\n"
- "SELECT $json_value as json_value;\n"
- "$returning = \"another string\";\n"
- "SELECT $returning;\n"
- "SELECT 1 as returning;\n"
- "SELECT $returning as returning;\n"
- );
-
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(JsonValueValidCaseHandlers) {
- const TVector<std::pair<TString, TString>> testCases = {
- {"", "'DefaultValue (Null)"},
- {"NULL", "'DefaultValue (Null)"},
- {"ERROR", "'Error (Null)"},
- {"DEFAULT 123", "'DefaultValue (Int32 '\"123\")"},
- };
-
- for (const auto& onEmpty : testCases) {
- for (const auto& onError : testCases) {
- TStringBuilder query;
- query << "$json = CAST(@@{\"key\": 1238}@@ as Json);\n"
- << "SELECT JSON_VALUE($json, \"strict $.key\"";
- if (!onEmpty.first.empty()) {
- query << " " << onEmpty.first << " ON EMPTY";
- }
- if (!onError.first.empty()) {
- query << " " << onError.first << " ON ERROR";
- }
- query << ");\n";
-
- NYql::TAstParseResult res = SqlToYql(query);
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(onEmpty.second + " " + onError.second));
- };
-
- TWordCountHive elementStat({"JsonValue"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonValue"] > 0);
- }
- }
- }
-
- Y_UNIT_TEST(JsonValueTooManyCaseHandlers) {
- NYql::TAstParseResult res = SqlToYql(
- "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" NULL ON EMPTY NULL ON ERROR NULL ON EMPTY);\n"
- );
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(
- Err2Str(res),
- "<main>:1:52: Error: Only 1 ON EMPTY and/or 1 ON ERROR clause is expected\n"
- );
- }
-
- Y_UNIT_TEST(JsonValueTooManyOnEmpty) {
- NYql::TAstParseResult res = SqlToYql(
- "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" NULL ON EMPTY NULL ON EMPTY);\n"
- );
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(
- Err2Str(res),
- "<main>:1:52: Error: Only 1 ON EMPTY clause is expected\n"
- );
- }
-
- Y_UNIT_TEST(JsonValueTooManyOnError) {
- NYql::TAstParseResult res = SqlToYql(
- "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" NULL ON ERROR NULL ON ERROR);\n"
- );
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(
- Err2Str(res),
- "<main>:1:52: Error: Only 1 ON ERROR clause is expected\n"
- );
- }
-
- Y_UNIT_TEST(JsonValueOnEmptyAfterOnError) {
- NYql::TAstParseResult res = SqlToYql(
- "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" NULL ON ERROR NULL ON EMPTY);\n"
- );
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(
- Err2Str(res),
- "<main>:1:52: Error: ON EMPTY clause must be before ON ERROR clause\n"
- );
- }
-
- Y_UNIT_TEST(JsonValueNullInput) {
- NYql::TAstParseResult res = SqlToYql(R"(SELECT JSON_VALUE(NULL, "strict $.key");)");
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Nothing (OptionalType (DataType 'Json)))"));
- };
-
- TWordCountHive elementStat({"JsonValue"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonValue"] > 0);
- }
-}
-
-Y_UNIT_TEST_SUITE(JsonExists) {
- Y_UNIT_TEST(JsonExistsValidHandlers) {
- const TVector<std::pair<TString, TString>> testCases = {
- {"", "(Just (Bool '\"false\"))"},
- {"TRUE ON ERROR", "(Just (Bool '\"true\"))"},
- {"FALSE ON ERROR", "(Just (Bool '\"false\"))"},
- {"UNKNOWN ON ERROR", "(Nothing (OptionalType (DataType 'Bool)))"},
- // NOTE: in this case we expect arguments of JsonExists callable to end immediately
- // after variables. This parenthesis at the end of the expression is left on purpose
- {"ERROR ON ERROR", "(Utf8 '\"strict $.key\") (JsonVariables))"},
- };
-
- for (const auto& item : testCases) {
- NYql::TAstParseResult res = SqlToYql(
- TStringBuilder() << R"(
- $json = CAST(@@{"key": 1238}@@ as Json);
- SELECT JSON_EXISTS($json, "strict $.key" )" << item.first << ");\n"
- );
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(item.second));
- };
-
- TWordCountHive elementStat({"JsonExists"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonExists"] > 0);
- }
- }
-
- Y_UNIT_TEST(JsonExistsInvalidHandler) {
- NYql::TAstParseResult res = SqlToYql(R"(
- $json = CAST(@@{"key": 1238}@@ as Json);
- $default = false;
- SELECT JSON_EXISTS($json, "strict $.key" $default ON ERROR);
- )");
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:53: Error: Unexpected token absence : Missing RPAREN \n\n");
- }
-
- Y_UNIT_TEST(JsonExistsNullInput) {
- NYql::TAstParseResult res = SqlToYql(R"(SELECT JSON_EXISTS(NULL, "strict $.key");)");
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Nothing (OptionalType (DataType 'Json)))"));
- };
-
- TWordCountHive elementStat({"JsonExists"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonExists"] > 0);
- }
-}
-
-Y_UNIT_TEST_SUITE(JsonQuery) {
- Y_UNIT_TEST(JsonQueryValidHandlers) {
- using TTestSuite = const TVector<std::pair<TString, TString>>;
- TTestSuite wrapCases = {
- {"", "'NoWrap"},
- {"WITHOUT WRAPPER", "'NoWrap"},
- {"WITHOUT ARRAY WRAPPER", "'NoWrap"},
- {"WITH WRAPPER", "'Wrap"},
- {"WITH ARRAY WRAPPER", "'Wrap"},
- {"WITH UNCONDITIONAL WRAPPER", "'Wrap"},
- {"WITH UNCONDITIONAL ARRAY WRAPPER", "'Wrap"},
- {"WITH CONDITIONAL WRAPPER", "'ConditionalWrap"},
- {"WITH CONDITIONAL ARRAY WRAPPER", "'ConditionalWrap"},
- };
- TTestSuite handlerCases = {
- {"", "'Null"},
- {"ERROR", "'Error"},
- {"NULL", "'Null"},
- {"EMPTY ARRAY", "'EmptyArray"},
- {"EMPTY OBJECT", "'EmptyObject"},
- };
-
- for (const auto& wrap : wrapCases) {
- for (const auto& onError : handlerCases) {
- for (const auto& onEmpty : handlerCases) {
- TStringBuilder query;
- query << R"($json = CAST(@@{"key": [123]}@@ as Json);
- SELECT JSON_QUERY($json, "strict $.key" )" << wrap.first;
- if (!onEmpty.first.empty()) {
- if (wrap.first.StartsWith("WITH ")) {
- continue;
- }
- query << " " << onEmpty.first << " ON EMPTY";
- }
- if (!onError.first.empty()) {
- query << " " << onError.first << " ON ERROR";
- }
- query << ");\n";
-
- NYql::TAstParseResult res = SqlToYql(query);
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- const TString args = TStringBuilder() << wrap.second << " " << onEmpty.second << " " << onError.second;
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(args));
- };
-
- Cout << wrap.first << " " << onEmpty.first << " " << onError.first << Endl;
-
- TWordCountHive elementStat({"JsonQuery"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonQuery"] > 0);
- }
- }
- }
- }
-
- Y_UNIT_TEST(JsonQueryOnEmptyWithWrapper) {
- NYql::TAstParseResult res = SqlToYql(R"(
- $json = CAST(@@{"key": 1238}@@ as Json);
- SELECT JSON_QUERY($json, "strict $" WITH ARRAY WRAPPER EMPTY ARRAY ON EMPTY);
- )");
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:38: Error: ON EMPTY is prohibited because WRAPPER clause is specified\n");
- }
-
- Y_UNIT_TEST(JsonQueryNullInput) {
- NYql::TAstParseResult res = SqlToYql(R"(SELECT JSON_QUERY(NULL, "strict $.key");)");
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Nothing (OptionalType (DataType 'Json)))"));
- };
-
- TWordCountHive elementStat({"JsonQuery"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonQuery"] > 0);
- }
-}
-
-Y_UNIT_TEST_SUITE(JsonPassing) {
- Y_UNIT_TEST(SupportedVariableTypes) {
- const TVector<TString> functions = {"JSON_EXISTS", "JSON_VALUE", "JSON_QUERY"};
-
- for (const auto& function : functions) {
- const auto query = Sprintf(R"(
- pragma CompactNamedExprs;
- $json = CAST(@@{"key": 1238}@@ as Json);
- SELECT %s(
- $json,
- "strict $.key"
- PASSING
- "string" as var1,
- 1.234 as var2,
- CAST(1 as Int64) as var3,
- true as var4,
- $json as var5
- ))",
- function.data()
- );
- NYql::TAstParseResult res = SqlToYql(query);
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var1" (String '"string")))"), "Cannot find `var1`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var2" (Double '"1.234")))"), "Cannot find `var2`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var3" (SafeCast (Int32 '"1") (DataType 'Int64))))"), "Cannot find `var3`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var4" (Bool '"true")))"), "Cannot find `var4`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var5" namedexprnode0))"), "Cannot find `var5`");
- };
-
- TWordCountHive elementStat({"JsonVariables"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonVariables"] > 0);
- }
- }
-
- Y_UNIT_TEST(ValidVariableNames) {
- const TVector<TString> functions = {"JSON_EXISTS", "JSON_VALUE", "JSON_QUERY"};
-
- for (const auto& function : functions) {
- const auto query = Sprintf(R"(
- $json = CAST(@@{"key": 1238}@@ as Json);
- SELECT %s(
- $json,
- "strict $.key"
- PASSING
- "one" as var1,
- "two" as "VaR2",
- "three" as `var3`,
- "four" as VaR4
- ))",
- function.data()
- );
- NYql::TAstParseResult res = SqlToYql(query);
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var1" (String '"one")))"), "Cannot find `var1`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"VaR2" (String '"two")))"), "Cannot find `VaR2`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var3" (String '"three")))"), "Cannot find `var3`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"VaR4" (String '"four")))"), "Cannot find `VaR4`");
- };
-
- TWordCountHive elementStat({"JsonVariables"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonVariables"] > 0);
- }
- }
-}
-
-Y_UNIT_TEST_SUITE(MigrationToJsonApi) {
- Y_UNIT_TEST(WarningOnDeprecatedJsonUdf) {
- NYql::TAstParseResult res = SqlToYql(R"(
- $json = CAST(@@{"key": 1234}@@ as Json);
- SELECT Json::Parse($json);
- )");
-
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:26: Warning: Json UDF is deprecated. Please use JSON API instead, code: 4506\n");
- }
-}
-
-Y_UNIT_TEST_SUITE(AnsiIdentsNegative) {
- Y_UNIT_TEST(EnableAnsiLexerFromRequestSpecialComments) {
- auto req = "\n"
- "\t --!ansi_lexer \n"
- "-- Some comment\n"
- "-- another comment\n"
- "pragma SimpleColumns;\n"
- "\n"
- "select 1, '''' as empty;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(AnsiLexerShouldNotBeEnabledHere) {
- auto req = "$str = '\n"
- "--!ansi_lexer\n"
- "--!syntax_v1\n"
- "';\n"
- "\n"
- "select 1, $str, \"\" as empty;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(DoubleQuotesInDictsTuplesOrLists) {
- auto req = "$d = { 'a': 1, \"b\": 2, 'c': 3,};";
-
- auto res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:16: Error: Column reference \"b\" is not allowed in current scope\n");
-
- req = "$t = (1, 2, \"a\");";
-
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:13: Error: Column reference \"a\" is not allowed in current scope\n");
-
- req = "$l = ['a', 'b', \"c\"];";
-
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: Column reference \"c\" is not allowed in current scope\n");
- }
-
- Y_UNIT_TEST(MultilineComments) {
- auto req = "/*/**/ select 1;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:16: Error: Unexpected character : syntax error...\n\n");
-
- req = "/*\n"
- "--/*\n"
- "*/ select 1;";
- res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:12: Error: Unexpected character : syntax error...\n\n");
-
- req = "/*\n"
- "/*\n"
- "--*/\n"
- "*/ select 1;";
- res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:0: Error: Unexpected token '*' : cannot match to any predicted input...\n\n");
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(res.Root);
- }
-}
-
-Y_UNIT_TEST_SUITE(AnsiOptionalAs) {
- Y_UNIT_TEST(OptionalAsInProjection) {
- UNIT_ASSERT(SqlToYql("PRAGMA AnsiOptionalAs; SELECT a b, c FROM plato.Input;").IsOk());
- ExpectFailWithError("PRAGMA DisableAnsiOptionalAs;\n"
- "SELECT a b, c FROM plato.Input;",
- "<main>:2:10: Error: Expecting mandatory AS here. Did you miss comma? Please add PRAGMA AnsiOptionalAs; for ANSI compatibility\n");
- }
-
- Y_UNIT_TEST(OptionalAsWithKeywords) {
- UNIT_ASSERT(SqlToYql("PRAGMA AnsiOptionalAs; SELECT a type, b data, c source FROM plato.Input;").IsOk());
- }
-}
-
-Y_UNIT_TEST_SUITE(SessionWindowNegative) {
- Y_UNIT_TEST(SessionWindowWithoutSource) {
- ExpectFailWithError("SELECT 1 + SessionWindow(ts, 32);",
- "<main>:1:12: Error: SessionWindow requires data source\n");
- }
-
- Y_UNIT_TEST(SessionWindowInProjection) {
- ExpectFailWithError("SELECT 1 + SessionWindow(ts, 32) from plato.Input;",
- "<main>:1:12: Error: SessionWindow can only be used as a top-level GROUP BY / PARTITION BY expression\n");
- }
-
- Y_UNIT_TEST(SessionWindowWithNonConstSecondArg) {
- ExpectFailWithError(
- "SELECT key, session_start FROM plato.Input\n"
- "GROUP BY SessionWindow(ts, 32 + subkey) as session_start, key;",
-
- "<main>:2:10: Error: Source does not allow column references\n"
- "<main>:2:33: Error: Column reference 'subkey'\n");
- }
-
- Y_UNIT_TEST(SessionWindowWithWrongNumberOfArgs) {
- ExpectFailWithError("SELECT * FROM plato.Input GROUP BY SessionWindow()",
- "<main>:1:36: Error: SessionWindow requires either two or four arguments\n");
- ExpectFailWithError("SELECT * FROM plato.Input GROUP BY SessionWindow(key, subkey, 100)",
- "<main>:1:36: Error: SessionWindow requires either two or four arguments\n");
- }
-
- Y_UNIT_TEST(DuplicateSessionWindow) {
- ExpectFailWithError(
- "SELECT\n"
- " *\n"
- "FROM plato.Input\n"
- "GROUP BY\n"
- " SessionWindow(ts, 10),\n"
- " user,\n"
- " SessionWindow(ts, 20)\n"
- ";",
-
- "<main>:7:5: Error: Duplicate session window specification:\n"
- "<main>:5:5: Error: Previous session window is declared here\n");
-
- ExpectFailWithError(
- "SELECT\n"
- " MIN(key) over w\n"
- "FROM plato.Input\n"
- "WINDOW w AS (\n"
- " PARTITION BY SessionWindow(ts, 10), user,\n"
- " SessionWindow(ts, 20)\n"
- ");",
-
- "<main>:6:5: Error: Duplicate session window specification:\n"
- "<main>:5:18: Error: Previous session window is declared here\n");
- }
-
- Y_UNIT_TEST(SessionStartStateWithoutSource) {
- ExpectFailWithError("SELECT 1 + SessionStart();",
- "<main>:1:12: Error: SessionStart requires data source\n");
- ExpectFailWithError("SELECT 1 + SessionState();",
- "<main>:1:12: Error: SessionState requires data source\n");
- }
-
- Y_UNIT_TEST(SessionStartStateWithoutGroupByOrWindow) {
- ExpectFailWithError("SELECT 1 + SessionStart() from plato.Input;",
- "<main>:1:12: Error: SessionStart can not be used without aggregation by SessionWindow\n");
- ExpectFailWithError("SELECT 1 + SessionState() from plato.Input;",
- "<main>:1:12: Error: SessionState can not be used without aggregation by SessionWindow\n");
- }
-
- Y_UNIT_TEST(SessionStartStateWithGroupByWithoutSession) {
- ExpectFailWithError("SELECT 1 + SessionStart() from plato.Input group by user;",
- "<main>:1:12: Error: SessionStart can not be used here: SessionWindow specification is missing in GROUP BY\n");
- ExpectFailWithError("SELECT 1 + SessionState() from plato.Input group by user;",
- "<main>:1:12: Error: SessionState can not be used here: SessionWindow specification is missing in GROUP BY\n");
- }
-
- Y_UNIT_TEST(SessionStartStateWithoutOverWithWindowWithoutSession) {
- ExpectFailWithError("SELECT 1 + SessionStart(), MIN(key) over w from plato.Input window w as ()",
- "<main>:1:12: Error: SessionStart can not be used without aggregation by SessionWindow. Maybe you forgot to add OVER `window_name`?\n");
- ExpectFailWithError("SELECT 1 + SessionState(), MIN(key) over w from plato.Input window w as ()",
- "<main>:1:12: Error: SessionState can not be used without aggregation by SessionWindow. Maybe you forgot to add OVER `window_name`?\n");
- }
-
- Y_UNIT_TEST(SessionStartStateWithWindowWithoutSession) {
- ExpectFailWithError("SELECT 1 + SessionStart() over w, MIN(key) over w from plato.Input window w as ()",
- "<main>:1:12: Error: SessionStart can not be used with window w: SessionWindow specification is missing in PARTITION BY\n");
- ExpectFailWithError("SELECT 1 + SessionState() over w, MIN(key) over w from plato.Input window w as ()",
- "<main>:1:12: Error: SessionState can not be used with window w: SessionWindow specification is missing in PARTITION BY\n");
- }
-
- Y_UNIT_TEST(SessionStartStateWithSessionedWindow) {
- ExpectFailWithError("SELECT 1 + SessionStart(), MIN(key) over w from plato.Input group by key window w as (partition by SessionWindow(ts, 1)) ",
- "<main>:1:12: Error: SessionStart can not be used here: SessionWindow specification is missing in GROUP BY. Maybe you forgot to add OVER `window_name`?\n");
- ExpectFailWithError("SELECT 1 + SessionState(), MIN(key) over w from plato.Input group by key window w as (partition by SessionWindow(ts, 1)) ",
- "<main>:1:12: Error: SessionState can not be used here: SessionWindow specification is missing in GROUP BY. Maybe you forgot to add OVER `window_name`?\n");
- }
-
- Y_UNIT_TEST(AggregationBySessionStateIsNotSupportedYet) {
- ExpectFailWithError("SELECT SOME(1 + SessionState()), key from plato.Input group by key, SessionWindow(ts, 1);",
- "<main>:1:17: Error: SessionState with GROUP BY is not supported yet\n");
- }
-
- Y_UNIT_TEST(SessionWindowInRtmr) {
- NYql::TAstParseResult res = SqlToYql(
- "SELECT * FROM plato.Input GROUP BY SessionWindow(ts, 10);",
- 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:54: Error: Streaming group by query must have a hopping window specification.\n");
-
- res = SqlToYql(R"(
- SELECT key, SUM(value) AS value FROM plato.Input
- GROUP BY key, HOP(subkey, "PT10S", "PT30S", "PT20S"), SessionWindow(ts, 10);
- )", 10, TString(NYql::RtmrProviderName));
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:13: Error: SessionWindow is unsupported for streaming sources\n");
- }
-}
-
-Y_UNIT_TEST_SUITE(LibraSqlSugar) {
- auto makeResult = [](TStringBuf settings) {
- return SqlToYql(
- TStringBuilder()
- << settings
- << "\n$udf1 = MyLibra::MakeLibraPreprocessor($settings);"
- << "\n$udf2 = CustomLibra::MakeLibraPreprocessor($settings);"
- << "\nPROCESS plato.Input USING $udf1(TableRow())"
- << "\nUNION ALL"
- << "\nPROCESS plato.Input USING $udf2(TableRow());"
- );
- };
-
- Y_UNIT_TEST(EmptySettings) {
- auto res = makeResult(R"(
- $settings = AsStruct();
- )");
- UNIT_ASSERT(res.IsOk());
- }
-
- Y_UNIT_TEST(OnlyEntities) {
- auto res = makeResult(R"(
- $settings = AsStruct(
- AsList("A", "B", "C") AS Entities
- );
- )");
- UNIT_ASSERT(res.IsOk());
- }
-
- Y_UNIT_TEST(EntitiesWithStrategy) {
- auto res = makeResult(R"(
- $settings = AsStruct(
- AsList("A", "B", "C") AS Entities,
- "blacklist" AS EntitiesStrategy
- );
- )");
- UNIT_ASSERT(res.IsOk());
- }
-
- Y_UNIT_TEST(AllSettings) {
- auto res = makeResult(R"(
- $settings = AsStruct(
- AsList("A", "B", "C") AS Entities,
- "whitelist" AS EntitiesStrategy,
- "path" AS BlockstatDict,
- false AS ParseWithFat,
- "map" AS Mode
- );
- )");
- UNIT_ASSERT(res.IsOk());
- }
-
- Y_UNIT_TEST(BadStrategy) {
- auto res = makeResult(R"(
- $settings = AsStruct("bad" AS EntitiesStrategy);
- )");
- UNIT_ASSERT_STRING_CONTAINS(
- Err2Str(res),
- "Error: MakeLibraPreprocessor got invalid entities strategy: expected 'whitelist' or 'blacklist'"
- );
- }
-
- Y_UNIT_TEST(BadEntities) {
- auto res = makeResult(R"(
- $settings = AsStruct(AsList("A", 1) AS Entities);
- )");
- UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "Error: MakeLibraPreprocessor entity must be string literal");
- }
-}
-
-Y_UNIT_TEST_SUITE(TrailingQuestionsNegative) {
- Y_UNIT_TEST(Basic) {
- ExpectFailWithError("SELECT 1?;", "<main>:1:9: Error: Unexpected token '?' at the end of expression\n");
- ExpectFailWithError("SELECT 1? + 1;", "<main>:1:10: Error: Unexpected token '+' : cannot match to any predicted input...\n\n");
- ExpectFailWithError("SELECT 1 + 1??? < 2", "<main>:1:13: Error: Unexpected token '?' at the end of expression\n");
- ExpectFailWithError("SELECT 1? > 2? > 3?",
- "<main>:1:11: Error: Unexpected token '?' at the end of expression\n"
- "<main>:1:16: Error: Unexpected token '?' at the end of expression\n"
- "<main>:1:21: Error: Unexpected token '?' at the end of expression\n");
- }
-
- Y_UNIT_TEST(SmartParen) {
- ExpectFailWithError("$x = 1; SELECT (Int32?, $x?)", "<main>:1:27: Error: Unexpected token '?' at the end of expression\n");
- ExpectFailWithError("SELECT (Int32, foo?)", "<main>:1:19: Error: Unexpected token '?' at the end of expression\n");
- }
-
- Y_UNIT_TEST(LambdaOptArgs) {
- ExpectFailWithError("$l = ($x, $y?, $z??, $t?) -> ($x);", "<main>:1:18: Error: Expecting at most one '?' token here (for optional lambda parameters), but got 2\n");
- }
-}
-
-Y_UNIT_TEST_SUITE(FlexibleTypes) {
- Y_UNIT_TEST(AssumeOrderByType) {
- UNIT_ASSERT(SqlToYql("PRAGMA FlexibleTypes; SELECT 1 AS int32 ASSUME ORDER BY int32").IsOk());
- }
-
- Y_UNIT_TEST(GroupingSets) {
- UNIT_ASSERT(SqlToYql("PRAGMA FlexibleTypes; SELECT COUNT(*) AS cnt, text, uuid FROM plato.Input GROUP BY GROUPING SETS((uuid), (uuid, text));").IsOk());
- }
-
- Y_UNIT_TEST(WeakField) {
- UNIT_ASSERT(SqlToYql("PRAGMA FlexibleTypes; SELECT WeakField(text, string) as text FROM plato.Input").IsOk());
- }
-
- Y_UNIT_TEST(Aggregation1) {
- TString q =
- "PRAGMA FlexibleTypes;\n"
- "$foo = ($x, $const, $type) -> ($x || $const || FormatType($type));\n"
- "SELECT $foo(SOME(x), 'aaa', String) FROM plato.Input GROUP BY y;";
- UNIT_ASSERT(SqlToYql(q).IsOk());
- }
-
- Y_UNIT_TEST(Aggregation2) {
- TString q =
- "PRAGMA FlexibleTypes;\n"
- "SELECT 1 + String + MAX(key) FROM plato.Input;";
- UNIT_ASSERT(SqlToYql(q).IsOk());
- }
-}
-
-Y_UNIT_TEST_SUITE(ExternalDeclares) {
- Y_UNIT_TEST(BasicUsage) {
- NSQLTranslation::TTranslationSettings settings;
- settings.DeclaredNamedExprs["foo"] = "String";
- auto res = SqlToYqlWithSettings("select $foo;", settings);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "declare") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"__((declare "$foo" (DataType 'String)))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("declare"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["declare"]);
- }
-
- Y_UNIT_TEST(DeclareOverrides) {
- NSQLTranslation::TTranslationSettings settings;
- settings.DeclaredNamedExprs["foo"] = "String";
- auto res = SqlToYqlWithSettings("declare $foo as Int32; select $foo;", settings);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "declare") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"__((declare "$foo" (DataType 'Int32)))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("declare"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["declare"]);
- }
-
- Y_UNIT_TEST(UnusedDeclareDoesNotProduceWarning) {
- NSQLTranslation::TTranslationSettings settings;
- settings.DeclaredNamedExprs["foo"] = "String";
- auto res = SqlToYqlWithSettings("select 1;", settings);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "declare") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"__((declare "$foo" (DataType 'String)))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("declare"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["declare"]);
- }
-
- Y_UNIT_TEST(DeclaresWithInvalidTypesFails) {
- NSQLTranslation::TTranslationSettings settings;
- settings.DeclaredNamedExprs["foo"] = "List<BadType>";
- auto res = SqlToYqlWithSettings("select 1;", settings);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:0:5: Error: Unknown type: 'BadType'\n"
- "<main>: Error: Failed to parse type for externally declared name 'foo'\n");
- }
-}
-
-Y_UNIT_TEST_SUITE(ExternalDataSource) {
- Y_UNIT_TEST(CreateExternalDataSourceWithAuthNone) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="NONE"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"NONE") '('"location" '"my-bucket") '('"source_type" '"ObjectStorage"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithAuthServiceAccount) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="SERVICE_ACCOUNT",
- SERVICE_ACCOUNT_ID="sa",
- SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"SERVICE_ACCOUNT") '('"location" '"my-bucket") '('"service_account_id" '"sa") '('"service_account_secret_name" '"sa_secret_name") '('"source_type" '"ObjectStorage"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithBasic) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="BASIC",
- LOGIN="admin",
- PASSWORD_SECRET_NAME="secret_name"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"BASIC") '('"location" '"protocol://host:port/") '('"login" '"admin") '('"password_secret_name" '"secret_name") '('"source_type" '"PostgreSQL"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithMdbBasic) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="MDB_BASIC",
- SERVICE_ACCOUNT_ID="sa",
- SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name",
- LOGIN="admin",
- PASSWORD_SECRET_NAME="secret_name"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"MDB_BASIC") '('"location" '"protocol://host:port/") '('"login" '"admin") '('"password_secret_name" '"secret_name") '('"service_account_id" '"sa") '('"service_account_secret_name" '"sa_secret_name") '('"source_type" '"PostgreSQL"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithAws) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="AWS",
- AWS_ACCESS_KEY_ID_SECRET_NAME="secred_id_name",
- AWS_SECRET_ACCESS_KEY_SECRET_NAME="secret_key_name",
- AWS_REGION="ru-central-1"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"AWS") '('"aws_access_key_id_secret_name" '"secred_id_name") '('"aws_region" '"ru-central-1") '('"aws_secret_access_key_secret_name" '"secret_key_name") '('"location" '"protocol://host:port/") '('"source_type" '"PostgreSQL"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithToken) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="YT",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="TOKEN",
- TOKEN_SECRET_NAME="token_name"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"TOKEN") '('"location" '"protocol://host:port/") '('"source_type" '"YT") '('"token_secret_name" '"token_name"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithTablePrefix) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- pragma TablePathPrefix='/aba';
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="NONE"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, "/aba/MyDataSource");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceIfNotExists) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE IF NOT EXISTS MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="NONE"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"NONE") '('"location" '"my-bucket") '('"source_type" '"ObjectStorage"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObjectIfNotExists"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AlterExternalDataSource) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER EXTERNAL DATA SOURCE MyDataSource
- SET (SOURCE_TYPE = "ObjectStorage", Login = "Admin"),
- SET Location "bucket",
- RESET (Auth_Method, Service_Account_Id, Service_Account_Secret_Name);
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alterObject))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('features '('('"location" '"bucket") '('"login" '"Admin") '('"source_type" '"ObjectStorage"))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('resetFeatures '('"auth_method" '"service_account_id" '"service_account_secret_name")))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceOrReplace) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- CREATE OR REPLACE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="NONE"
- );
- )");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"NONE") '('"location" '"my-bucket") '('"source_type" '"ObjectStorage"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObjectOrReplace"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateOrReplaceForUnsupportedTableTypesShouldFail) {
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE OR REPLACE TABLE t (a int32 not null, primary key(a, a));
- )sql" , "<main>:3:23: Error: OR REPLACE feature is supported only for EXTERNAL DATA SOURCE and EXTERNAL TABLE\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE OR REPLACE TABLE t (
- Key Uint64,
- Value1 String,
- PRIMARY KEY (Key)
- )
- WITH (
- STORE = COLUMN,
- AUTO_PARTITIONING_MIN_PARTITIONS_COUNT = 10
- );
- )sql" , "<main>:3:23: Error: OR REPLACE feature is supported only for EXTERNAL DATA SOURCE and EXTERNAL TABLE\n");
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithBadArguments) {
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource;
- )sql" , "<main>:3:56: Error: Unexpected token ';' : syntax error...\n\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- LOCATION="my-bucket",
- AUTH_METHOD="NONE"
- );
- )sql" , "<main>:5:33: Error: SOURCE_TYPE requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket"
- );
- )sql" , "<main>:5:30: Error: AUTH_METHOD requires key\n");
-
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="NONE1"
- );
- )sql" , "<main>:6:33: Error: Unknown AUTH_METHOD = NONE1\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="SERVICE_ACCOUNT"
- );
- )sql" , "<main>:6:33: Error: SERVICE_ACCOUNT_ID requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="SERVICE_ACCOUNT",
- SERVICE_ACCOUNT_ID="s1"
- );
- )sql" , "<main>:7:40: Error: SERVICE_ACCOUNT_SECRET_NAME requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="SERVICE_ACCOUNT",
- SERVICE_ACCOUNT_SECRET_NAME="s1"
- );
- )sql" , "<main>:7:49: Error: SERVICE_ACCOUNT_ID requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="BASIC",
- LOGIN="admin"
- );
- )sql" , "<main>:7:27: Error: PASSWORD_SECRET_NAME requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="BASIC",
- PASSWORD_SECRET_NAME="secret_name"
- );
- )sql" , "<main>:7:42: Error: LOGIN requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="MDB_BASIC",
- SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name",
- LOGIN="admin",
- PASSWORD_SECRET_NAME="secret_name"
- );
- )sql" , "<main>:9:42: Error: SERVICE_ACCOUNT_ID requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="MDB_BASIC",
- SERVICE_ACCOUNT_ID="sa",
- LOGIN="admin",
- PASSWORD_SECRET_NAME="secret_name"
- );
- )sql" , "<main>:9:42: Error: SERVICE_ACCOUNT_SECRET_NAME requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="MDB_BASIC",
- SERVICE_ACCOUNT_ID="sa",
- SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name",
- PASSWORD_SECRET_NAME="secret_name"
- );
- )sql" , "<main>:9:42: Error: LOGIN requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="MDB_BASIC",
- SERVICE_ACCOUNT_ID="sa",
- SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name",
- LOGIN="admin"
- );
- )sql" , "<main>:9:27: Error: PASSWORD_SECRET_NAME requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="AWS",
- AWS_SECRET_ACCESS_KEY_SECRET_NAME="secret_key_name",
- AWS_REGION="ru-central-1"
- );
- )sql" , "<main>:8:32: Error: AWS_ACCESS_KEY_ID_SECRET_NAME requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="AWS",
- AWS_ACCESS_KEY_ID_SECRET_NAME="secred_id_name",
- AWS_REGION="ru-central-1"
- );
- )sql" , "<main>:8:32: Error: AWS_SECRET_ACCESS_KEY_SECRET_NAME requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="AWS",
- AWS_SECRET_ACCESS_KEY_SECRET_NAME="secret_key_name",
- AWS_ACCESS_KEY_ID_SECRET_NAME="secred_id_name"
- );
- )sql" , "<main>:8:51: Error: AWS_REGION requires key\n");
- }
-
- Y_UNIT_TEST(DropExternalDataSourceWithTablePrefix) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP EXTERNAL DATA SOURCE MyDataSource;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropExternalDataSource) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- pragma TablePathPrefix='/aba';
- DROP EXTERNAL DATA SOURCE MyDataSource;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, "/aba/MyDataSource");
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropExternalDataSourceIfExists) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP EXTERNAL DATA SOURCE IF EXISTS MyDataSource;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, "MyDataSource");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObjectIfExists"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(ExternalTable) {
- Y_UNIT_TEST(CreateExternalTable) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable (
- a int
- ) WITH (
- DATA_SOURCE="/Root/mydatasource",
- LOCATION="/folder1/*"
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('data_source_path (String '"/Root/mydatasource")) '('location (String '"/folder1/*")))) '('tableType 'externalTable)))))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tablescheme"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalTableWithTablePrefix) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- pragma TablePathPrefix='/aba';
- CREATE EXTERNAL TABLE mytable (
- a int
- ) WITH (
- DATA_SOURCE="mydatasource",
- LOCATION="/folder1/*"
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, "/aba/mydatasource");
- UNIT_ASSERT_STRING_CONTAINS(line, "/aba/mytable");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tablescheme"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalTableObjectStorage) {
- auto res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable (
- a int,
- year Int
- ) WITH (
- DATA_SOURCE="/Root/mydatasource",
- LOCATION="/folder1/*",
- FORMAT="json_as_string",
- `projection.enabled`="true",
- `projection.year.type`="integer",
- `projection.year.min`="2010",
- `projection.year.max`="2022",
- `projection.year.interval`="1",
- `projection.month.type`="integer",
- `projection.month.min`="1",
- `projection.month.max`="12",
- `projection.month.interval`="1",
- `projection.month.digits`="2",
- `storage.location.template`="${year}/${month}",
- PARTITONED_BY = "[year, month]"
- );
- )sql");
- UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString());
- }
-
- Y_UNIT_TEST(CreateExternalTableIfNotExists) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE IF NOT EXISTS mytable (
- a int
- ) WITH (
- DATA_SOURCE="/Root/mydatasource",
- LOCATION="/folder1/*"
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('data_source_path (String '"/Root/mydatasource")) '('location (String '"/folder1/*")))) '('tableType 'externalTable)))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, "create_if_not_exists");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalTableOrReplace) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- CREATE OR REPLACE EXTERNAL TABLE mytable (
- a int
- ) WITH (
- DATA_SOURCE="/Root/mydatasource",
- LOCATION="/folder1/*"
- );
- )");
- UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('data_source_path (String '"/Root/mydatasource")) '('location (String '"/folder1/*")))) '('tableType 'externalTable)))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, "create_or_replace");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AlterExternalTableAddColumn) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER EXTERNAL TABLE mytable
- ADD COLUMN my_column int32,
- RESET (LOCATION);
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('actions '('('addColumns '('('"my_column" (AsOptionalType (DataType 'Int32))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('setTableSettings '('('location)))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('tableType 'externalTable))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alter))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AlterExternalTableDropColumn) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER EXTERNAL TABLE mytable
- DROP COLUMN my_column,
- SET (Location = "abc", Other_Prop = "42"),
- SET x 'y';
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('actions '('('dropColumns '('"my_column")#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('setTableSettings '('('location (String '"abc")) '('Other_Prop (String '"42")) '('x (String '"y")))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('tableType 'externalTable))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alter))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalTableWithBadArguments) {
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable;
- )sql" , "<main>:3:45: Error: Unexpected token ';' : syntax error...\n\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable (
- a int
- );
- )sql" , "<main>:4:23: Error: DATA_SOURCE requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable (
- a int
- ) WITH (
- DATA_SOURCE="/Root/mydatasource"
- );
- )sql" , "<main>:6:33: Error: LOCATION requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable (
- a int
- ) WITH (
- LOCATION="/folder1/*"
- );
- )sql" , "<main>:6:30: Error: DATA_SOURCE requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable (
- a int,
- PRIMARY KEY(a)
- ) WITH (
- DATA_SOURCE="/Root/mydatasource",
- LOCATION="/folder1/*"
- );
- )sql" , "<main>:8:30: Error: PRIMARY KEY is not supported for external table\n");
- }
-
- Y_UNIT_TEST(DropExternalTable) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP EXTERNAL TABLE MyExternalTable;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("tablescheme"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropExternalTableWithTablePrefix) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- pragma TablePathPrefix='/aba';
- DROP EXTERNAL TABLE MyExternalTable;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, "/aba/MyExternalTable");
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'tablescheme"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropExternalTableIfExists) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP EXTERNAL TABLE IF EXISTS MyExternalTable;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("tablescheme"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop_if_exists"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(TopicsDDL) {
- void TestQuery(const TString& query, bool expectOk = true) {
- TStringBuilder finalQuery;
-
- finalQuery << "use plato;" << Endl << query;
- auto res = SqlToYql(finalQuery, 10, "kikimr");
- if (expectOk) {
- UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString());
- } else {
- UNIT_ASSERT(!res.IsOk());
- }
- }
-
- Y_UNIT_TEST(CreateTopicSimple) {
- TestQuery(R"(
- CREATE TOPIC topic1;
- )");
- TestQuery(R"(
- CREATE TOPIC `cluster1.topic1`;
- )");
- TestQuery(R"(
- CREATE TOPIC topic1 WITH (metering_mode = "str_value", partition_count_limit = 123, retention_period = Interval('PT1H'));
- )");
- }
-
- Y_UNIT_TEST(CreateTopicConsumer) {
- TestQuery(R"(
- CREATE TOPIC topic1 (CONSUMER cons1);
- )");
- TestQuery(R"(
- CREATE TOPIC topic1 (CONSUMER cons1, CONSUMER cons2 WITH (important = false));
- )");
- TestQuery(R"(
- CREATE TOPIC topic1 (CONSUMER cons1, CONSUMER cons2 WITH (important = false)) WITH (supported_codecs = "1,2,3");
- )");
- }
-
- Y_UNIT_TEST(AlterTopicSimple) {
- TestQuery(R"(
- ALTER TOPIC topic1 SET (retention_period = Interval('PT1H'));
- )");
- TestQuery(R"(
- ALTER TOPIC topic1 SET (retention_storage_mb = 3, partition_count_limit = 50);
- )");
- TestQuery(R"(
- ALTER TOPIC topic1 RESET (supported_codecs, retention_period);
- )");
- TestQuery(R"(
- ALTER TOPIC topic1 RESET (partition_write_speed_bytes_per_second),
- SET (partition_write_burst_bytes = 11111, min_active_partitions = 1);
- )");
- }
- Y_UNIT_TEST(AlterTopicConsumer) {
- TestQuery(R"(
- ALTER TOPIC topic1 ADD CONSUMER consumer1,
- ADD CONSUMER consumer2 WITH (important = false, supported_codecs = "RAW"),
- ALTER CONSUMER consumer3 SET (important = false, read_from = 1),
- ALTER CONSUMER consumer3 RESET (supported_codecs),
- DROP CONSUMER consumer4,
- SET (partition_count_limit = 11, retention_period = Interval('PT1H')),
- RESET(metering_mode)
- )");
- }
- Y_UNIT_TEST(DropTopic) {
- TestQuery(R"(
- DROP TOPIC topic1;
- )");
- }
-
- Y_UNIT_TEST(TopicBadRequests) {
- TestQuery(R"(
- CREATE TOPIC topic1();
- )", false);
- TestQuery(R"(
- CREATE TOPIC topic1 SET setting1 = value1;
- )", false);
- TestQuery(R"(
- ALTER TOPIC topic1 SET setting1 value1;
- )", false);
- TestQuery(R"(
- ALTER TOPIC topic1 RESET setting1;
- )", false);
-
- TestQuery(R"(
- ALTER TOPIC topic1 DROP CONSUMER consumer4 WITH (k1 = v1);
- )", false);
-
- TestQuery(R"(
- CREATE TOPIC topic1 WITH (retention_period = 123);
- )", false);
- TestQuery(R"(
- CREATE TOPIC topic1 (CONSUMER cons1, CONSUMER cons1 WITH (important = false));
- )", false);
- TestQuery(R"(
- CREATE TOPIC topic1 (CONSUMER cons1 WITH (bad_option = false));
- )", false);
- TestQuery(R"(
- ALTER TOPIC topic1 ADD CONSUMER cons1, ALTER CONSUMER cons1 RESET (important);
- )", false);
- TestQuery(R"(
- ALTER TOPIC topic1 ADD CONSUMER consumer1,
- ALTER CONSUMER consumer3 SET (supported_codecs = "RAW", read_from = 1),
- ALTER CONSUMER consumer3 RESET (supported_codecs);
- )", false);
- TestQuery(R"(
- ALTER TOPIC topic1 ADD CONSUMER consumer1,
- ALTER CONSUMER consumer3 SET (supported_codecs = "RAW", read_from = 1),
- ALTER CONSUMER consumer3 SET (read_from = 2);
- )", false);
- }
-
- Y_UNIT_TEST(TopicWithPrefix) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- PRAGMA TablePathPrefix = '/database/path/to/tables';
- ALTER TOPIC `my_table/my_feed` ADD CONSUMER `my_consumer`;
- )");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("/database/path/to/tables/my_table/my_feed"), 0}, {"topic", 0}};
- VerifyProgram(res, elementStat);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["topic"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["/database/path/to/tables/my_table/my_feed"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(BlockEnginePragma) {
- Y_UNIT_TEST(Basic) {
- const TVector<TString> values = {"auto", "force", "disable"};
- for (const auto& value : values) {
- const auto query = TStringBuilder() << "pragma Blockengine='" << value << "'; select 1;";
- NYql::TAstParseResult res = SqlToYql(query);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_STRING_CONTAINS(line, TStringBuilder() << R"(Configure! world (DataSource '"config") '"BlockEngine" '")" << value << "\"");
- };
-
- TWordCountHive elementStat({"BlockEngine"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["BlockEngine"] == ((value == "disable") ? 0 : 1));
- }
- }
-
- Y_UNIT_TEST(UnknownSetting) {
- ExpectFailWithError("use plato; pragma BlockEngine='foo';",
- "<main>:1:31: Error: Expected `disable|auto|force' argument for: BlockEngine\n");
- }
-}
-
-Y_UNIT_TEST_SUITE(TViewSyntaxTest) {
- Y_UNIT_TEST(CreateViewSimple) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- CREATE VIEW TheView WITH (security_invoker = TRUE) AS SELECT 1;
- )"
- );
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
- }
-
- Y_UNIT_TEST(CreateViewIfNotExists) {
- constexpr const char* name = "TheView";
- NYql::TAstParseResult res = SqlToYql(std::format(R"(
- USE plato;
- CREATE VIEW IF NOT EXISTS {} WITH (security_invoker = TRUE) AS SELECT 1;
- )", name
- ));
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_STRING_CONTAINS(line, name);
- UNIT_ASSERT_STRING_CONTAINS(line, "createObjectIfNotExists");
- }
- };
-
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(CreateViewFromTable) {
- constexpr const char* path = "/PathPrefix/TheView";
- constexpr const char* query = R"(
- SELECT * FROM SomeTable
- )";
-
- NYql::TAstParseResult res = SqlToYql(std::format(R"(
- USE plato;
- CREATE VIEW `{}` WITH (security_invoker = TRUE) AS {};
- )",
- path,
- query
- )
- );
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_STRING_CONTAINS(line, path);
- UNIT_ASSERT_STRING_CONTAINS(line, "createObject");
- }
- };
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(CheckReconstructedQuery) {
- constexpr const char* path = "/PathPrefix/TheView";
- constexpr const char* query = R"(
- SELECT * FROM FirstTable JOIN SecondTable ON FirstTable.key == SecondTable.key
- )";
-
- NYql::TAstParseResult res = SqlToYql(std::format(R"(
- USE plato;
- CREATE VIEW `{}` WITH (security_invoker = TRUE) AS {};
- )",
- path,
- query
- )
- );
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TString reconstructedQuery = ToString(Tokenize(query));
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- if (word == "query_text") {
- UNIT_ASSERT_STRING_CONTAINS(line, reconstructedQuery);
- }
- };
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(DropView) {
- constexpr const char* path = "/PathPrefix/TheView";
- NYql::TAstParseResult res = SqlToYql(std::format(R"(
- USE plato;
- DROP VIEW `{}`;
- )",
- path
- )
- );
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_STRING_CONTAINS(line, path);
- UNIT_ASSERT_STRING_CONTAINS(line, "dropObject");
- }
- };
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(DropViewIfExists) {
- constexpr const char* name = "TheView";
- NYql::TAstParseResult res = SqlToYql(std::format(R"(
- USE plato;
- DROP VIEW IF EXISTS {};
- )", name
- ));
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_STRING_CONTAINS(line, name);
- UNIT_ASSERT_STRING_CONTAINS(line, "dropObjectIfExists");
- }
- };
-
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(CreateViewWithTablePrefix) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- PRAGMA TablePathPrefix='/PathPrefix';
- CREATE VIEW TheView WITH (security_invoker = TRUE) AS SELECT 1;
- )"
- );
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_STRING_CONTAINS(line, "/PathPrefix/TheView");
- UNIT_ASSERT_STRING_CONTAINS(line, "createObject");
- }
- };
-
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(DropViewWithTablePrefix) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- PRAGMA TablePathPrefix='/PathPrefix';
- DROP VIEW TheView;
- )"
- );
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, "/PathPrefix/TheView");
- UNIT_ASSERT_STRING_CONTAINS(line, "dropObject");
- }
- };
-
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(YtAlternativeSchemaSyntax) {
- NYql::TAstParseResult res = SqlToYql(R"(
- SELECT * FROM plato.Input WITH schema(y Int32, x String not null);
- )");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "userschema") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__('('('"userschema" (StructType '('"y" (AsOptionalType (DataType 'Int32))) '('"x" (DataType 'String))))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("userschema"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["userschema"]);
- }
-
- Y_UNIT_TEST(UseViewAndFullColumnId) {
- NYql::TAstParseResult res = SqlToYql("USE plato; SELECT Input.x FROM Input VIEW uitzicht;");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("SqlAccess"), 0}, {"SqlProjectItem", 0}, {"Read!", 0}};
- VerifyProgram(res, elementStat);
- UNIT_ASSERT_VALUES_EQUAL(0, elementStat["SqlAccess"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Read!"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(CompactNamedExprs) {
- Y_UNIT_TEST(SourceCallablesInWrongContext) {
- TString query = R"(
- pragma CompactNamedExprs;
- $foo = %s();
- select $foo from plato.Input;
- )";
-
- THashMap<TString, TString> errs = {
- {"TableRow", "<main>:3:20: Error: TableRow requires data source\n"},
- {"JoinTableRow", "<main>:3:20: Error: JoinTableRow requires data source\n"},
- {"TableRecordIndex", "<main>:3:20: Error: Unable to use function: TableRecord without source\n"},
- {"TablePath", "<main>:3:20: Error: Unable to use function: TablePath without source\n"},
- {"SystemMetadata", "<main>:3:20: Error: Unable to use function: SystemMetadata without source\n"},
- };
-
- for (TString callable : { "TableRow", "JoinTableRow", "TableRecordIndex", "TablePath", "SystemMetadata"}) {
- auto req = Sprintf(query.c_str(), callable.c_str());
- ExpectFailWithError(req, errs[callable]);
- }
- }
-
- Y_UNIT_TEST(ValidateUnusedExprs) {
- TString query = R"(
- pragma warning("disable", "4527");
- pragma CompactNamedExprs;
- pragma ValidateUnusedExprs;
-
- $foo = count(1);
- select 1;
- )";
- ExpectFailWithError(query, "<main>:6:20: Error: Aggregation is not allowed in this context\n");
- query = R"(
- pragma warning("disable", "4527");
- pragma CompactNamedExprs;
- pragma ValidateUnusedExprs;
-
- define subquery $x() as
- select count(1, 2);
- end define;
- select 1;
- )";
- ExpectFailWithError(query, "<main>:7:24: Error: Aggregation function Count requires exactly 1 argument(s), given: 2\n");
- }
-
- Y_UNIT_TEST(DisableValidateUnusedExprs) {
- TString query = R"(
- pragma warning("disable", "4527");
- pragma CompactNamedExprs;
- pragma DisableValidateUnusedExprs;
-
- $foo = count(1);
- select 1;
- )";
- SqlToYql(query).IsOk();
- query = R"(
- pragma warning("disable", "4527");
- pragma CompactNamedExprs;
- pragma DisableValidateUnusedExprs;
-
- define subquery $x() as
- select count(1, 2);
- end define;
- select 1;
- )";
- SqlToYql(query).IsOk();
- }
-}
-
-Y_UNIT_TEST_SUITE(ResourcePool) {
- Y_UNIT_TEST(CreateResourcePool) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE RESOURCE POOL MyResourcePool WITH (
- CONCURRENT_QUERY_LIMIT=20,
- QUERY_CANCEL_AFTER_SECONDS=86400,
- QUEUE_TYPE="FIFO"
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"concurrent_query_limit" (Int32 '"20")) '('"query_cancel_after_seconds" (Int32 '"86400")) '('"queue_type" '"FIFO"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateResourcePoolWithBadArguments) {
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE RESOURCE POOL MyResourcePool;
- )sql" , "<main>:3:51: Error: Unexpected token ';' : syntax error...\n\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE RESOURCE POOL MyResourcePool WITH (
- DUPLICATE_SETTING="first_value",
- DUPLICATE_SETTING="second_value"
- );
- )sql" , "<main>:5:21: Error: DUPLICATE_SETTING duplicate keys\n");
- }
-
- Y_UNIT_TEST(AlterResourcePool) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER RESOURCE POOL MyResourcePool
- SET (CONCURRENT_QUERY_LIMIT = 30, Weight = 5, QUEUE_TYPE = "UNORDERED"),
- RESET (Query_Cancel_After_Seconds, Query_Count_Limit);
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alterObject))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('features '('('"concurrent_query_limit" (Int32 '"30")) '('"queue_type" '"UNORDERED") '('"weight" (Int32 '"5")))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('resetFeatures '('"query_cancel_after_seconds" '"query_count_limit")))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropResourcePool) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP RESOURCE POOL MyResourcePool;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(BackupCollection) {
- Y_UNIT_TEST(CreateBackupCollection) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TestCollection WITH (
- STORAGE="local",
- TAG="test" -- for testing purposes, not a real thing
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"storage" (String '"local")))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"tag" (String '"test"))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('entries '()))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'create"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateBackupCollectionWithDatabase) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TestCollection DATABASE WITH (
- STORAGE="local",
- TAG="test" -- for testing purposes, not a real thing
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"storage" (String '"local")))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"tag" (String '"test"))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('entries '('('('type 'database)))))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'create"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateBackupCollectionWithTables) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TestCollection (
- TABLE someTable,
- TABLE `prefix/anotherTable`
- ) WITH (
- STORAGE="local",
- TAG="test" -- for testing purposes, not a real thing
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"storage" (String '"local")))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"tag" (String '"test"))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('entries '('('('type 'table) '('path '"someTable")) '('('type 'table) '('path '"prefix/anotherTable")))))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'create"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateBackupCollectionWithBadArguments) {
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TestCollection;
- )sql" , "<main>:3:55: Error: Unexpected token ';' : syntax error...\n\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TABLE TestCollection;
- )sql" , "<main>:3:47: Error: Unexpected token 'TestCollection' : syntax error...\n\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION DATABASE `test` TestCollection;
- )sql" , "<main>:3:50: Error: Unexpected token '`test`' : syntax error...\n\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TestCollection WITH (
- DUPLICATE_SETTING="first_value",
- DUPLICATE_SETTING="second_value"
- );
- )sql" , "<main>:5:21: Error: DUPLICATE_SETTING duplicate keys\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TestCollection WITH (
- INT_SETTING=1
- );
- )sql" , "<main>:4:21: Error: INT_SETTING value should be a string literal\n");
- }
-
- Y_UNIT_TEST(AlterBackupCollection) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER BACKUP COLLECTION TestCollection
- SET (STORAGE="remote"), -- also just for test
- SET (TAG1 = "123"),
- RESET (TAG2, TAG3);
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alter))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"storage" (String '"remote")))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"tag1" (String '"123"))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('resetSettings '('"tag2" '"tag3")))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AlterBackupCollectionEntries) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER BACKUP COLLECTION TestCollection
- DROP TABLE `test`,
- ADD DATABASE;
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alter))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('alterEntries)#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('type 'table) '('path '"test") '('action 'drop)))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('type 'database) '('action 'add)))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropBackupCollection) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP BACKUP COLLECTION TestCollection;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(ResourcePoolClassifier) {
- Y_UNIT_TEST(CreateResourcePoolClassifier) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE RESOURCE POOL CLASSIFIER MyResourcePoolClassifier WITH (
- RANK=20,
- RESOURCE_POOL='wgUserQueries',
- MEMBER_NAME='yandex_query@abc'
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"member_name" '"yandex_query@abc") '('"rank" (Int32 '"20")) '('"resource_pool" '"wgUserQueries"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateResourcePoolClassifierWithBadArguments) {
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE RESOURCE POOL CLASSIFIER MyResourcePoolClassifier;
- )sql" , "<main>:3:72: Error: Unexpected token ';' : syntax error...\n\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE RESOURCE POOL CLASSIFIER MyResourcePoolClassifier WITH (
- DUPLICATE_SETTING="first_value",
- DUPLICATE_SETTING="second_value"
- );
- )sql" , "<main>:5:21: Error: DUPLICATE_SETTING duplicate keys\n");
- }
-
- Y_UNIT_TEST(AlterResourcePoolClassifier) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER RESOURCE POOL CLASSIFIER MyResourcePoolClassifier
- SET (RANK = 30, Weight = 5, MEMBER_NAME = "test@user"),
- RESET (Resource_Pool);
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alterObject))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('features '('('"member_name" '"test@user") '('"rank" (Int32 '"30")) '('"weight" (Int32 '"5")))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('resetFeatures '('"resource_pool")))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropResourcePoolClassifier) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP RESOURCE POOL CLASSIFIER MyResourcePoolClassifier;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(BacktickMatching) {
- auto req = "select\n"
- " 1 as `Schema has \\`RealCost\\``\n"
- " -- foo`bar";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- req = "select 1 as `a``b`, 2 as ````, 3 as `\\x60a\\x60`, 4 as ```b```, 5 as `\\`c\\``";
- res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-}
-
-Y_UNIT_TEST_SUITE(OlapPartitionCount) {
- Y_UNIT_TEST(CorrectUsage) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE TABLE `mytable` (id Uint32, PRIMARY KEY (id))
- PARTITION BY HASH(id)
- WITH (STORE = COLUMN, PARTITION_COUNT = 8);
- )sql");
-
- UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString());
- }
-
- Y_UNIT_TEST(UseWithoutColumnStore) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE TABLE `mytable` (id Uint32, PRIMARY KEY (id))
- WITH (PARTITION_COUNT = 8);
- )sql");
-
- UNIT_ASSERT(!res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 1);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "PARTITION_COUNT can be used only with STORE=COLUMN");
- }
-}
-
-Y_UNIT_TEST_SUITE(Backup) {
- Y_UNIT_TEST(Simple) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- BACKUP TestCollection;
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'Incremental"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'backup"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(Incremental) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- BACKUP TestCollection INCREMENTAL;
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'backupIncremental"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(Restore) {
- Y_UNIT_TEST(Simple) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- RESTORE TestCollection;
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'restore"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AtPoint) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- RESTORE TestCollection AT '2024-06-16_20-14-02';
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('at '"2024-06-16_20-14-02")#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'restore"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(ColumnFamily) {
- Y_UNIT_TEST(CompressionLevelCorrectUsage) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- CREATE TABLE tableName (
- Key Uint32 FAMILY default,
- Value String FAMILY family1,
- PRIMARY KEY (Key),
- FAMILY default (
- DATA = "test",
- COMPRESSION = "lz4",
- COMPRESSION_LEVEL = 5
- ),
- FAMILY family1 (
- DATA = "test",
- COMPRESSION = "lz4",
- COMPRESSION_LEVEL = 3
- )
- );
- )");
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("compression_level"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("5"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("3"));
- }
- };
-
- TWordCountHive elementStat = { { TString("Write"), 0 }, { TString("compression_level"), 0 } };
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["compression_level"]);
- }
-
- Y_UNIT_TEST(FieldDataIsNotString) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- CREATE TABLE tableName (
- Key Uint32 FAMILY default,
- PRIMARY KEY (Key),
- FAMILY default (
- DATA = 1,
- COMPRESSION = "lz4",
- COMPRESSION_LEVEL = 5
- )
- );
- )");
- UNIT_ASSERT(!res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 1);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "DATA value should be a string literal");
- }
-
- Y_UNIT_TEST(FieldCompressionIsNotString) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- CREATE TABLE tableName (
- Key Uint32 FAMILY default,
- PRIMARY KEY (Key),
- FAMILY default (
- DATA = "test",
- COMPRESSION = 2,
- COMPRESSION_LEVEL = 5
- ),
- );
- )");
- UNIT_ASSERT(!res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 1);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "COMPRESSION value should be a string literal");
- }
-
- Y_UNIT_TEST(FieldCompressionLevelIsNotInteger) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- CREATE TABLE tableName (
- Key Uint32 FAMILY default,
- PRIMARY KEY (Key),
- FAMILY default (
- DATA = "test",
- COMPRESSION = "lz4",
- COMPRESSION_LEVEL = "5"
- )
- );
- )");
- UNIT_ASSERT(!res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 1);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "COMPRESSION_LEVEL value should be an integer");
- }
-
- Y_UNIT_TEST(AlterCompressionCorrectUsage) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- ALTER TABLE tableName ALTER FAMILY default SET COMPRESSION "lz4";
- )");
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(AlterCompressionFieldIsNotString) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- ALTER TABLE tableName ALTER FAMILY default SET COMPRESSION lz4;
- )");
- UNIT_ASSERT(!res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 1);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "Unexpected token 'lz4' : cannot match to any predicted input");
- }
-
- Y_UNIT_TEST(AlterCompressionLevelCorrectUsage) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- ALTER TABLE tableName ALTER FAMILY default SET COMPRESSION_LEVEL 5;
- )");
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(AlterCompressionLevelFieldIsNotInteger) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- ALTER TABLE tableName ALTER FAMILY default SET COMPRESSION_LEVEL "5";
- )");
- UNIT_ASSERT(!res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 1);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "COMPRESSION_LEVEL value should be an integer");
- }
-}
+#define ANTLR_VER 3
+#include "sql_ut_common.h"
Y_UNIT_TEST_SUITE(QuerySplit) {
Y_UNIT_TEST(Simple) {
@@ -8207,42 +95,3 @@ Y_UNIT_TEST_SUITE(QuerySplit) {
};)");
}
}
-
-Y_UNIT_TEST_SUITE(MatchRecognizeMeasuresAggregation) {
- Y_UNIT_TEST(InsideSelect) {
- ExpectFailWithError(R"sql(
- SELECT FIRST(0), LAST(1);
- )sql",
- "<main>:2:20: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- "<main>:2:30: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- );
- }
-
- Y_UNIT_TEST(OutsideSelect) {
- ExpectFailWithError(R"sql(
- $lambda = ($x) -> (FIRST($x) + LAST($x));
- SELECT $lambda(x) FROM plato.Input;
- )sql",
- "<main>:2:32: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- "<main>:2:44: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- );
- }
-
- Y_UNIT_TEST(AsAggregateFunction) {
- ExpectFailWithError(R"sql(
- SELECT FIRST(x), LAST(x) FROM plato.Input;
- )sql",
- "<main>:2:20: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- "<main>:2:30: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- );
- }
-
- Y_UNIT_TEST(AsWindowFunction) {
- ExpectFailWithError(R"sql(
- SELECT FIRST(x) OVER(), LAST(x) OVER() FROM plato.Input;
- )sql",
- "<main>:2:20: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- "<main>:2:37: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- );
- }
-}
diff --git a/yql/essentials/sql/v1/sql_ut_antlr4.cpp b/yql/essentials/sql/v1/sql_ut_antlr4.cpp
index 6d54a4f409..18480d048c 100644
--- a/yql/essentials/sql/v1/sql_ut_antlr4.cpp
+++ b/yql/essentials/sql/v1/sql_ut_antlr4.cpp
@@ -30,8093 +30,10 @@ TParsedTokenList Tokenize(const TString& query) {
return tokens;
}
-TString ToString(const TParsedTokenList& tokens) {
- TStringBuilder reconstructedQuery;
- for (const auto& token : tokens) {
- if (token.Name == "WS" || token.Name == "EOF") {
- continue;
- }
- if (!reconstructedQuery.empty()) {
- reconstructedQuery << ' ';
- }
- reconstructedQuery << token.Content;
- }
- return reconstructedQuery;
-}
-
-}
-
-Y_UNIT_TEST_SUITE(AnsiMode) {
- Y_UNIT_TEST(PragmaAnsi) {
- UNIT_ASSERT(SqlToYql("PRAGMA ANSI 2016;").IsOk());
- }
-}
-
-Y_UNIT_TEST_SUITE(SqlParsingOnly) {
- ///This function is used in BACKWARD COMPATIBILITY tests below that LIMIT the sets of token that CAN NOT be used
- ///as identifiers in different contexts in a SQL request
- ///\return list of tokens that failed this check
- TVector<TString> ValidateTokens(const THashSet<TString>& forbidden, const std::function<TString (const TString& )>& makeRequest) {
- THashMap<TString, bool> allTokens;
- for (const auto& t: NSQLFormat::GetKeywords()) {
- allTokens[t] = !forbidden.contains((t));
- }
- for (const auto& f: forbidden) {
- UNIT_ASSERT(allTokens.contains(f)); //check that forbidden list contains tokens only(argument check)
- }
- TVector<TString> failed;
- for (const auto& [token, allowed]: allTokens) {
- if (SqlToYql(makeRequest(token)).IsOk() != allowed)
- failed.push_back(token);
- }
- return failed;
- }
-
- Y_UNIT_TEST(TokensAsColumnName) { //id_expr
- auto failed = ValidateTokens({
- "ALL", "ANY", "AS", "ASSUME", "ASYMMETRIC", "AUTOMAP", "BETWEEN", "BITCAST",
- "CALLABLE", "CASE", "CAST", "CUBE", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
- "DICT", "DISTINCT", "ENUM", "ERASE", "EXCEPT", "EXISTS", "FLOW", "FROM", "FULL",
- "HAVING", "HOP", "INTERSECT", "JSON_EXISTS", "JSON_QUERY", "JSON_VALUE", "LIMIT", "LIST",
- "NOT", "OPTIONAL", "PROCESS", "REDUCE", "REPEATABLE", "RESOURCE", "RETURN", "RETURNING", "ROLLUP",
- "SELECT", "SET", "STREAM", "STRUCT", "SYMMETRIC", "TAGGED", "TUPLE", "UNBOUNDED",
- "UNION", "VARIANT", "WHEN", "WHERE", "WINDOW", "WITHOUT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT " << token << " FROM Plato.Input";
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsWithoutColumnName) { //id_without
- auto failed = ValidateTokens({
- "ALL", "AS", "ASSUME", "ASYMMETRIC", "AUTOMAP", "BETWEEN", "BITCAST",
- "CALLABLE", "CASE", "CAST", "CUBE", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
- "DICT", "DISTINCT", "EMPTY_ACTION", "ENUM", "EXCEPT", "EXISTS", "FALSE", "FLOW", "FROM", "FULL",
- "HAVING", "HOP", "INTERSECT", "JSON_EXISTS", "JSON_QUERY", "JSON_VALUE", "LIMIT", "LIST",
- "NOT", "NULL", "OPTIONAL", "PROCESS", "REDUCE", "REPEATABLE", "RESOURCE", "RETURN", "RETURNING", "ROLLUP",
- "SELECT", "SET", "STRUCT", "SYMMETRIC", "TAGGED", "TRUE", "TUPLE", "UNBOUNDED",
- "UNION", "VARIANT", "WHEN", "WHERE", "WINDOW", "WITHOUT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT * WITHOUT " << token << " FROM Plato.Input";
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsColumnNameInAddColumn) { //id_schema
- auto failed = ValidateTokens({
- "ANY", "AUTOMAP", "CALLABLE", "COLUMN", "DICT", "ENUM", "ERASE", "FALSE", "FLOW",
- "LIST", "OPTIONAL", "REPEATABLE", "RESOURCE",
- "SET", "STREAM", "STRUCT", "TAGGED", "TRUE", "TUPLE", "VARIANT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "ALTER TABLE Plato.Input ADD COLUMN " << token << " Bool";
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsColumnAlias) {
- auto failed = ValidateTokens({
- "AUTOMAP", "FALSE",
- "REPEATABLE", "TRUE"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT Col as " << token << " FROM Plato.Input";
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsTableName) { //id_table_or_type
- auto failed = ValidateTokens({
- "ANY", "AUTOMAP", "COLUMN", "ERASE", "FALSE",
- "REPEATABLE", "STREAM", "TRUE"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT * FROM Plato." << token;
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsTableAlias) { //id_table
- auto failed = ValidateTokens({
- "AUTOMAP", "CALLABLE", "DICT", "ENUM","FALSE", "FLOW",
- "LIST", "OPTIONAL", "REPEATABLE", "RESOURCE",
- "SET", "STRUCT", "TAGGED", "TRUE", "TUPLE", "VARIANT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT * FROM Plato.Input AS " << token;
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsHints) { //id_hint
- auto failed = ValidateTokens({
- "AUTOMAP", "CALLABLE", "COLUMNS", "DICT", "ENUM", "FALSE", "FLOW",
- "LIST", "OPTIONAL", "REPEATABLE", "RESOURCE",
- "SCHEMA", "SET", "STRUCT", "TAGGED", "TRUE", "TUPLE", "VARIANT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT * FROM Plato.Input WITH " << token;
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsWindow) { //id_window
- auto failed = ValidateTokens({
- "AUTOMAP", "CALLABLE", "DICT", "ENUM", "FALSE", "FLOW", "GROUPS", "LIST", "OPTIONAL",
- "RANGE", "REPEATABLE", "RESOURCE", "ROWS", "SET", "STRUCT", "TAGGED" ,"TRUE", "TUPLE", "VARIANT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT * FROM Plato.Input WINDOW " << token << " AS ()";
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TokensAsIdExprIn) { //id_expr_in
- auto failed = ValidateTokens({
- "ALL", "ANY", "AS", "ASSUME", "ASYMMETRIC", "AUTOMAP", "BETWEEN", "BITCAST",
- "CALLABLE", "CASE", "CAST", "COMPACT", "CUBE", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
- "DICT", "DISTINCT", "ENUM", "ERASE", "EXCEPT", "EXISTS", "FLOW", "FROM", "FULL",
- "HAVING", "HOP", "INTERSECT", "JSON_EXISTS", "JSON_QUERY", "JSON_VALUE", "LIMIT", "LIST",
- "NOT", "OPTIONAL", "PROCESS", "REDUCE", "REPEATABLE", "RESOURCE", "RETURN", "RETURNING", "ROLLUP",
- "SELECT", "SET", "STREAM", "STRUCT", "SYMMETRIC", "TAGGED", "TUPLE", "UNBOUNDED",
- "UNION", "VARIANT", "WHEN", "WHERE", "WINDOW", "WITHOUT"
- },
- [](const TString& token){
- TStringBuilder req;
- req << "SELECT * FROM Plato.Input WHERE q IN " << token;
- return req;
- }
- );
- UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
- }
-
- Y_UNIT_TEST(TableHints) {
- UNIT_ASSERT(SqlToYql("SELECT * FROM plato.Input WITH INFER_SCHEMA").IsOk());
- UNIT_ASSERT(SqlToYql("SELECT * FROM plato.Input WITH (INFER_SCHEMA)").IsOk());
- }
-
- Y_UNIT_TEST(InNoHints) {
- TString query = "SELECT * FROM plato.Input WHERE key IN (1,2,3)";
-
- VerifySqlInHints(query, { "'('('warnNoAnsi))" }, {});
- VerifySqlInHints(query, { "'()" }, false);
- VerifySqlInHints(query, { "'('('ansi))" }, true);
- }
-
- Y_UNIT_TEST(InHintCompact) {
- // should parse COMPACT as hint
- TString query = "SELECT * FROM plato.Input WHERE key IN COMPACT(1, 2, 3)";
-
- VerifySqlInHints(query, { "'('isCompact)" });
- }
-
- Y_UNIT_TEST(InHintSubquery) {
- // should parse tableSource as hint
- TString query = "$subq = (SELECT key FROM plato.Input); SELECT * FROM plato.Input WHERE key IN $subq";
-
- VerifySqlInHints(query, { "'('tableSource)" });
- }
-
- Y_UNIT_TEST(InHintCompactSubquery) {
- TString query = "$subq = (SELECT key FROM plato.Input); SELECT * FROM plato.Input WHERE key IN COMPACT $subq";
-
- VerifySqlInHints(query, { "'('isCompact)", "'('tableSource)" });
- }
-
- Y_UNIT_TEST(CompactKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("SELECT COMPACT FROM plato.Input WHERE COMPACT IN COMPACT(1, 2, 3)").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT * FROM COMPACT").IsOk());
- }
-
- Y_UNIT_TEST(FamilyKeywordNotReservedForNames) {
- // FIXME: check if we can get old behaviour
- //UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE FAMILY (FAMILY Uint32, PRIMARY KEY (FAMILY));").IsOk());
- //UNIT_ASSERT(SqlToYql("USE plato; SELECT FAMILY FROM FAMILY").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT FAMILY FROM Input").IsOk());
- }
-
- Y_UNIT_TEST(ResetKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE RESET (RESET Uint32, PRIMARY KEY (RESET));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT RESET FROM RESET").IsOk());
- }
-
- Y_UNIT_TEST(SyncKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE SYNC (SYNC Uint32, PRIMARY KEY (SYNC));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT SYNC FROM SYNC").IsOk());
- }
-
- Y_UNIT_TEST(AsyncKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE ASYNC (ASYNC Uint32, PRIMARY KEY (ASYNC));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT ASYNC FROM ASYNC").IsOk());
- }
-
- Y_UNIT_TEST(DisableKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE DISABLE (DISABLE Uint32, PRIMARY KEY (DISABLE));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT DISABLE FROM DISABLE").IsOk());
- }
-
- Y_UNIT_TEST(ChangefeedKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE CHANGEFEED (CHANGEFEED Uint32, PRIMARY KEY (CHANGEFEED));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT CHANGEFEED FROM CHANGEFEED").IsOk());
- }
-
- Y_UNIT_TEST(ReplicationKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE REPLICATION (REPLICATION Uint32, PRIMARY KEY (REPLICATION));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT REPLICATION FROM REPLICATION").IsOk());
- }
-
- Y_UNIT_TEST(TransferKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE TRANSFER (TRANSFER Uint32, PRIMARY KEY (TRANSFER));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT TRANSFER FROM TRANSFER").IsOk());
- }
-
- Y_UNIT_TEST(SecondsKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE SECONDS (SECONDS Uint32, PRIMARY KEY (SECONDS));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT SECONDS FROM SECONDS").IsOk());
- }
-
- Y_UNIT_TEST(MillisecondsKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE MILLISECONDS (MILLISECONDS Uint32, PRIMARY KEY (MILLISECONDS));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT MILLISECONDS FROM MILLISECONDS").IsOk());
- }
-
- Y_UNIT_TEST(MicrosecondsKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE MICROSECONDS (MICROSECONDS Uint32, PRIMARY KEY (MICROSECONDS));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT MICROSECONDS FROM MICROSECONDS").IsOk());
- }
-
- Y_UNIT_TEST(NanosecondsKeywordNotReservedForNames) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE NANOSECONDS (NANOSECONDS Uint32, PRIMARY KEY (NANOSECONDS));").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT NANOSECONDS FROM NANOSECONDS").IsOk());
- }
-
- Y_UNIT_TEST(Jubilee) {
- NYql::TAstParseResult res = SqlToYql("USE plato; INSERT INTO Arcadia (r2000000) VALUES (\"2M GET!!!\");");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(QualifiedAsteriskBefore) {
- NYql::TAstParseResult res = SqlToYql(
- "PRAGMA DisableSimpleColumns;"
- "select interested_table.*, LENGTH(value) AS megahelpful_len from plato.Input as interested_table;"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- static bool seenStar = false;
- if (word == "FlattenMembers") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("interested_table."));
- } else if (word == "SqlProjectItem") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("megahelpful_len")));
- UNIT_ASSERT_VALUES_EQUAL(seenStar, true);
- } else if (word == "SqlProjectStarItem") {
- seenStar = true;
- }
- };
- TWordCountHive elementStat = {{TString("FlattenMembers"), 0}, {TString("SqlProjectItem"), 0}, {TString("SqlProjectStarItem"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["FlattenMembers"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectStarItem"]);
- }
-
- Y_UNIT_TEST(QualifiedAsteriskAfter) {
- NYql::TAstParseResult res = SqlToYql(
- "PRAGMA DisableSimpleColumns;"
- "select LENGTH(value) AS megahelpful_len, interested_table.* from plato.Input as interested_table;"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- static bool seenStar = false;
- if (word == "FlattenMembers") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("interested_table."));
- } else if (word == "SqlProjectItem") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("megahelpful_len")));
- UNIT_ASSERT_VALUES_EQUAL(seenStar, false);
- } else if (word == "SqlProjectStarItem") {
- seenStar = true;
- }
- };
- TWordCountHive elementStat = {{TString("FlattenMembers"), 0}, {TString("SqlProjectItem"), 0}, {TString("SqlProjectStarItem"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["FlattenMembers"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectStarItem"]);
- }
-
- Y_UNIT_TEST(QualifiedMembers) {
- NYql::TAstParseResult res = SqlToYql("select interested_table.key, interested_table.value from plato.Input as interested_table;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- const bool fieldKey = TString::npos != line.find(Quote("key"));
- const bool fieldValue = TString::npos != line.find(Quote("value"));
- const bool refOnTable = TString::npos != line.find("interested_table.");
- if (word == "SqlProjectItem") {
- UNIT_ASSERT(fieldKey || fieldValue);
- UNIT_ASSERT(!refOnTable);
- } else if (word == "Write!") {
- UNIT_ASSERT(fieldKey && fieldValue && !refOnTable);
- }
- };
- TWordCountHive elementStat = {{TString("SqlProjectStarItem"), 0}, {TString("SqlProjectItem"), 0}, {TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(0, elementStat["SqlProjectStarItem"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(ExplainQueryPlan) {
- UNIT_ASSERT(SqlToYql("EXPLAIN SELECT 1;").IsOk());
- UNIT_ASSERT(SqlToYql("EXPLAIN QUERY PLAN SELECT 1;").IsOk());
- }
-
- Y_UNIT_TEST(JoinParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- "PRAGMA DisableSimpleColumns;"
- " SELECT table_bb.*, table_aa.key as megakey"
- " FROM plato.Input AS table_aa"
- " JOIN plato.Input AS table_bb"
- " ON table_aa.value == table_bb.value;"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "SelectMembers") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("table_aa."));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table_bb."));
- } else if (word == "SqlProjectItem") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("megakey")));
- } else if (word == "SqlColumn") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("table_aa")));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("key")));
- }
- };
- TWordCountHive elementStat = {{TString("SqlProjectItem"), 0}, {TString("SqlProjectStarItem"), 0}, {TString("SelectMembers"), 0}, {TString("SqlColumn"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectStarItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SelectMembers"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlColumn"]);
- }
-
- Y_UNIT_TEST(Join3Table) {
- NYql::TAstParseResult res = SqlToYql(
- " PRAGMA DisableSimpleColumns;"
- " SELECT table_bb.*, table_aa.key as gigakey, table_cc.* "
- " FROM plato.Input AS table_aa"
- " JOIN plato.Input AS table_bb ON table_aa.key == table_bb.key"
- " JOIN plato.Input AS table_cc ON table_aa.subkey == table_cc.subkey;"
- );
- Err2Str(res);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "SelectMembers") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("table_aa."));
- UNIT_ASSERT(line.find("table_bb.") != TString::npos || line.find("table_cc.") != TString::npos);
- } else if (word == "SqlProjectItem") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("gigakey")));
- } else if (word == "SqlColumn") {
- const auto posTableAA = line.find(Quote("table_aa"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, posTableAA);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("key")));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("table_aa", posTableAA + 3));
- }
- };
- TWordCountHive elementStat = {{TString("SqlProjectItem"), 0}, {TString("SqlProjectStarItem"), 0}, {TString("SelectMembers"), 0}, {TString("SqlColumn"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlProjectStarItem"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SelectMembers"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlColumn"]);
- }
-
- Y_UNIT_TEST(DisabledJoinCartesianProduct) {
- NYql::TAstParseResult res = SqlToYql("pragma DisableAnsiImplicitCrossJoin; use plato; select * from A,B,C");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:67: Error: Cartesian product of tables is disabled. Please use explicit CROSS JOIN or enable it via PRAGMA AnsiImplicitCrossJoin\n");
- }
-
- Y_UNIT_TEST(JoinCartesianProduct) {
- NYql::TAstParseResult res = SqlToYql("pragma AnsiImplicitCrossJoin; use plato; select * from A,B,C");
- UNIT_ASSERT(res.Root);
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "EquiJoin") {
- auto pos = line.find("Cross");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, pos);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Cross", pos + 1));
- }
- };
- TWordCountHive elementStat = {{TString("EquiJoin"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["EquiJoin"]);
- }
-
- Y_UNIT_TEST(CreateAlterUserWithLoginNoLogin) {
- auto reqCreateUser = SqlToYql(R"(
- USE plato;
- CREATE USER user1;
- )");
-
- UNIT_ASSERT(reqCreateUser.IsOk());
-
- auto reqAlterUser = SqlToYql(R"(
- USE plato;
- ALTER USER user1;
- )");
-
- UNIT_ASSERT(!reqAlterUser.IsOk());
- UNIT_ASSERT_STRING_CONTAINS(reqAlterUser.Issues.ToString(), "Error: mismatched input ';' expecting {ENCRYPTED, HASH, LOGIN, NOLOGIN, PASSWORD, RENAME, WITH}");
-
- auto reqPasswordAndLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 LOgin;
- )");
-
- UNIT_ASSERT(reqPasswordAndLogin.IsOk());
-
- auto reqPasswordAndNoLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 PASSWORD '123' NOLOGIN;
- )");
-
- UNIT_ASSERT(reqPasswordAndNoLogin.IsOk());
-
- auto reqLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 LOGIN;
- )");
-
- UNIT_ASSERT(reqLogin.IsOk());
-
- auto reqNoLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 NOLOGIN;
- )");
-
- UNIT_ASSERT(reqNoLogin.IsOk());
-
- auto reqLoginNoLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 LOGIN NOLOGIN;
- )");
-
- UNIT_ASSERT(!reqLoginNoLogin.IsOk());
- UNIT_ASSERT_STRING_CONTAINS(reqLoginNoLogin.Issues.ToString(), "Error: Conflicting or redundant options");
-
- auto reqAlterLoginNoLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 LOGIN;
- ALTER USER user1 NOLOGIN;
- )");
-
- UNIT_ASSERT(reqAlterLoginNoLogin.IsOk());
-
- auto reqAlterLoginNoLoginWithPassword = SqlToYql(R"(
- USE plato;
- CREATE USER user1 LOGIN;
- ALTER USER user1 PASSWORD '321' NOLOGIN;
- )");
-
- UNIT_ASSERT(reqAlterLoginNoLoginWithPassword.IsOk());
- }
-
- Y_UNIT_TEST(CreateUserWithHash) {
- auto reqCreateUser = SqlToYql(R"(
- USE plato;
- CREATE USER user1 HASH '{
- "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
- "salt": "U+tzBtgo06EBQCjlARA6Jg==",
- "type": "argon2id"
- }';
- )");
-
- UNIT_ASSERT(reqCreateUser.IsOk());
-
- auto reqCreateUserWithNoLogin = SqlToYql(R"(
- USE plato;
- CREATE USER user1 HASH '{
- "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
- "salt": "U+tzBtgo06EBQCjlARA6Jg==",
- "type": "argon2id"
- }'
- NOLOGIN;
- )");
-
- UNIT_ASSERT(reqCreateUserWithNoLogin.IsOk());
-
- auto reqCreateUserWithPassword = SqlToYql(R"(
- USE plato;
- CREATE USER user1 HASH '{
- "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
- "salt": "U+tzBtgo06EBQCjlARA6Jg==",
- "type": "argon2id"
- }'
- PASSWORD '123';
- )");
-
- UNIT_ASSERT(!reqCreateUserWithPassword.IsOk());
- UNIT_ASSERT_STRING_CONTAINS(reqCreateUserWithPassword.Issues.ToString(), "Error: Conflicting or redundant options");
-
- auto reqAlterUser = SqlToYql(R"(
- USE plato;
- CREATE USER user1;
- ALTER USER user1 HASH '{
- "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
- "salt": "U+tzBtgo06EBQCjlARA6Jg==",
- "type": "argon2id"
- }';
- )");
-
- UNIT_ASSERT(reqAlterUser.IsOk());
- }
-
- Y_UNIT_TEST(JoinWithoutConcreteColumns) {
- NYql::TAstParseResult res = SqlToYql(
- " use plato;"
- " SELECT a.v, b.value"
- " FROM `Input1` VIEW `ksv` AS a"
- " JOIN `Input2` AS b"
- " ON a.k == b.key;"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "SqlProjectItem") {
- UNIT_ASSERT(line.find(Quote("a.v")) != TString::npos || line.find(Quote("b.value")) != TString::npos);
- } else if (word == "SqlColumn") {
- const auto posTableA = line.find(Quote("a"));
- const auto posTableB = line.find(Quote("b"));
- if (posTableA != TString::npos) {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("v")));
- } else {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, posTableB);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("value")));
- }
- }
- };
- TWordCountHive elementStat = {{TString("SqlProjectStarItem"), 0}, {TString("SqlProjectItem"), 0}, {TString("SqlColumn"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(0, elementStat["SqlProjectStarItem"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlColumn"]);
- }
-
- Y_UNIT_TEST(JoinWithSameValues) {
- NYql::TAstParseResult res = SqlToYql("SELECT a.value, b.value FROM plato.Input AS a JOIN plato.Input as b ON a.key == b.key;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "SqlProjectItem") {
- const bool isValueFromA = TString::npos != line.find(Quote("a.value"));
- const bool isValueFromB = TString::npos != line.find(Quote("b.value"));
- UNIT_ASSERT(isValueFromA || isValueFromB);
- } if (word == "Write!") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("a.a."));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("b.b."));
- }
- };
- TWordCountHive elementStat = {{TString("SqlProjectStarItem"), 0}, {TString("SqlProjectItem"), 0}, {"Write!", 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(0, elementStat["SqlProjectStarItem"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(SameColumnsForDifferentTables) {
- NYql::TAstParseResult res = SqlToYql("SELECT a.key, b.key FROM plato.Input as a JOIN plato.Input as b on a.key==b.key;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SameColumnsForDifferentTablesFullJoin) {
- NYql::TAstParseResult res = SqlToYql("SELECT a.key, b.key, a.value, b.value FROM plato.Input AS a FULL JOIN plato.Input AS b USING(key);");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(JoinStreamLookupStrategyHint) {
- {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ StreamLookup() */ plato.Input AS b USING(key);");
- UNIT_ASSERT(res.Root);
- }
- //case insensitive
- {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ streamlookup() */ plato.Input AS b USING(key);");
- UNIT_ASSERT(res.Root);
- }
- }
-
- Y_UNIT_TEST(JoinConflictingStrategyHint) {
- {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ StreamLookup() */ /*+ Merge() */ plato.Input AS b USING(key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:91: Error: Conflicting join strategy hints\n");
- }
- }
-
- Y_UNIT_TEST(JoinDuplicatingStrategyHint) {
- {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ StreamLookup() */ /*+ StreamLookup() */ plato.Input AS b USING(key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:98: Error: Duplicate join strategy hint\n");
- }
- }
-
- Y_UNIT_TEST(WarnCrossJoinStrategyHint) {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a CROSS JOIN /*+ merge() */ plato.Input AS b;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:32: Warning: Non-default join strategy will not be used for CROSS JOIN, code: 4534\n");
- }
-
- Y_UNIT_TEST(WarnCartesianProductStrategyHint) {
- NYql::TAstParseResult res = SqlToYql("pragma AnsiImplicitCrossJoin; use plato; SELECT * FROM A, /*+ merge() */ B;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:74: Warning: Non-default join strategy will not be used for CROSS JOIN, code: 4534\n");
- }
-
- Y_UNIT_TEST(WarnUnknownJoinStrategyHint) {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ xmerge() */ plato.Input AS b USING (key);");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:41: Warning: Unsupported join hint: xmerge, code: 4534\n");
- }
-
- Y_UNIT_TEST(ReverseLabels) {
- NYql::TAstParseResult res = SqlToYql("select in.key as subkey, subkey as key from plato.Input as in;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(AutogenerationAliasWithoutCollisionConflict1) {
- NYql::TAstParseResult res = SqlToYql("select LENGTH(Value), key as column1 from plato.Input;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(AutogenerationAliasWithoutCollision2Conflict2) {
- NYql::TAstParseResult res = SqlToYql("select key as column0, LENGTH(Value) from plato.Input;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(InputAliasForQualifiedAsterisk) {
- NYql::TAstParseResult res = SqlToYql("use plato; select zyuzya.*, key from plato.Input as zyuzya;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectSupportsResultColumnsWithTrailingComma) {
- NYql::TAstParseResult res = SqlToYql("select a, b, c, from plato.Input;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectOrderByLabeledColumn) {
- NYql::TAstParseResult res = SqlToYql("pragma DisableOrderedColumns; select key as goal from plato.Input order by goal");
- UNIT_ASSERT(res.Root);
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "DataSource") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("plato"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Input"));
-
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("goal"));
- } else if (word == "Sort") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("goal"));
-
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("key"));
- }
- };
- TWordCountHive elementStat = {{TString("DataSource"), 0}, {TString("Sort"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["DataSource"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Sort"]);
- }
-
- Y_UNIT_TEST(SelectOrderBySimpleExpr) {
- NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by a + a");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectAssumeOrderByTableRowAccess) {
- NYql::TAstParseResult res = SqlToYql("$key = 'foo';select * from plato.Input assume order by TableRow().$key");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectOrderByDuplicateLabels) {
- NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by a, a");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectOrderByExpression) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input as i order by cast(key as uint32) + cast(subkey as uint32)");
- UNIT_ASSERT(res.Root);
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Sort") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"+MayWarn\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("key"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("subkey"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Bool 'true)"));
-
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("i.key"));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("i.subkey"));
- }
- };
- TWordCountHive elementStat = {{TString("Sort"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Sort"]);
- }
-
- Y_UNIT_TEST(SelectOrderByExpressionDesc) {
- NYql::TAstParseResult res = SqlToYql("pragma disablesimplecolumns; select i.*, key, subkey from plato.Input as i order by cast(i.key as uint32) - cast(i.subkey as uint32) desc");
- UNIT_ASSERT(res.Root);
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Sort") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"-MayWarn\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"subkey\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Bool 'false)"));
- } else if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'columns"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"subkey\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("prefix"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"i.\""));
- }
- };
- TWordCountHive elementStat = {{TString("Sort"), 0}, {TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Sort"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(SelectOrderByExpressionAsc) {
- NYql::TAstParseResult res = SqlToYql("select i.key, i.subkey from plato.Input as i order by cast(key as uint32) % cast(i.subkey as uint32) asc");
- UNIT_ASSERT(res.Root);
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Sort") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"%MayWarn\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"subkey\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Bool 'true)"));
- } else if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'columns"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"subkey\""));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("i."));
- }
- };
- TWordCountHive elementStat = {{TString("Sort"), 0}, {TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Sort"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(ReferenceToKeyInSubselect) {
- NYql::TAstParseResult res = SqlToYql("select b.key from (select a.key from plato.Input as a) as b;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(OrderByCastValue) {
- NYql::TAstParseResult res = SqlToYql("select i.key, i.subkey from plato.Input as i order by cast(key as uint32) desc;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(GroupByCastValue) {
- NYql::TAstParseResult res = SqlToYql("select count(1) from plato.Input as i group by cast(key as uint8);");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(KeywordInSelectColumns) {
- NYql::TAstParseResult res = SqlToYql("select in, s.check from (select 1 as in, \"test\" as check) as s;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectAllGroupBy) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input group by subkey;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(CreateObjectWithFeatures) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('\"K2\" '\"V2\") '('\"Key1\" '\"Value1\")"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(CreateObjectIfNotExists) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT IF NOT EXISTS secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObjectIfNotExists"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(CreateObjectWithFeaturesStrings) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET) WITH (Key1=\"Value1\", K2='V2', K3=V3, K4='', K5=`aaa`, K6='a\\'aa');");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('\"K2\" '\"V2\") '('\"K3\" '\"V3\") '('\"K4\" '\"\") '('\"K5\" '\"aaa\") '('\"K6\" '\"a'aa\") '('\"Key1\" '\"Value1\")"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}, {TString("SECRET"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- }
-
- Y_UNIT_TEST(UpsertObjectWithFeatures) {
- NYql::TAstParseResult res = SqlToYql("USE plato; UPSERT OBJECT secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('\"K2\" '\"V2\") '('\"Key1\" '\"Value1\")"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("upsertObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(CreateObjectWithFeaturesAndFlags) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2, RECURSE);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('\"Key1\" '\"Value1\") '('\"RECURSE\")"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(Select1Type) {
- NYql::TAstParseResult res = SqlToYql("SELECT 1 type;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectTableType) {
- NYql::TAstParseResult res = SqlToYql("USE plato; SELECT * from T type;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(CreateObjectNoFeatures) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(AlterObjectWithFeatures) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato;\n"
- "declare $path as String;\n"
- "ALTER OBJECT secretId (TYPE SECRET) SET (Key1=$path, K2=V2);"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"Key1\" (EvaluateAtom \"$path\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"K2\" '\"V2\""));
-
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alterObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(AlterObjectNoFeatures) {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER OBJECT secretId (TYPE SECRET);");
- UNIT_ASSERT(!res.Root);
- }
-
- Y_UNIT_TEST(DropObjectNoFeatures) {
- NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT secretId (TYPE SECRET);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(DropObjectWithFeatures) {
- NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT secretId (TYPE SECRET) WITH (A, B, C);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(DropObjectWithOneOption) {
- NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT secretId (TYPE SECRET) WITH OVERRIDE;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"OVERRIDE\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(DropObjectIfExists) {
- NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT IF EXISTS secretId (TYPE SECRET);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObjectIfExists"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
- }
-
- Y_UNIT_TEST(PrimaryKeyParseCorrect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE tableName (Key Uint32, Subkey Int64, Value String, PRIMARY KEY (Key, Subkey));");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"Key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"Subkey\""));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}, {TString("primarykey"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["primarykey"]);
- }
-
- Y_UNIT_TEST(AlterDatabaseAst) {
- NYql::TAstParseResult request = SqlToYql("USE plato; ALTER DATABASE `/Root/test` OWNER TO user1;");
- UNIT_ASSERT(request.IsOk());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- Y_UNUSED(word);
-
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(
- R"(let world (Write! world sink (Key '('databasePath (String '"/Root/test"))) (Void) '('('mode 'alterDatabase) '('owner '"user1"))))"
- ));
- };
-
- TWordCountHive elementStat({TString("\'mode \'alterDatabase")});
- VerifyProgram(request, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["\'mode \'alterDatabase"]);
- }
-
- Y_UNIT_TEST(CreateTableNonNullableYqlTypeAstCorrect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32 not null);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (DataType 'Int32) '('columnConstrains '('('not_null))) '())))))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableNullableYqlTypeAstCorrect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableNonNullablePgTypeAstCorrect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a pg_int4 not null);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (PgType '_int4) '('columnConstrains '('('not_null))) '())))))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableNullablePgTypeAstCorrect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a pg_int4);");
- UNIT_ASSERT(res.Root);
-
- res.Root->PrettyPrintTo(Cout, PRETTY_FLAGS);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (PgType '_int4)) '('columnConstrains '()) '()))))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableNullPkColumnsAreAllowed) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32, primary key(a));");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableNotNullPkColumnsAreIdempotentAstCorrect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32 not null, primary key(a));");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (DataType 'Int32) '('columnConstrains '('('not_null))) '()))) '('primarykey '('"a"))))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableWithIfNotExists) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE IF NOT EXISTS t (a int32, primary key(a));");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create_if_not_exists) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTempTable) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TEMP TABLE t (a int32, primary key(a));");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")) '('temporary))))__"), line);
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTemporaryTable) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TEMPORARY TABLE t (a int32, primary key(a));");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos,
- line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")) '('temporary))))__"), line);
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- }
-
- Y_UNIT_TEST(CreateTableWithoutTypes) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a, primary key(a));");
- UNIT_ASSERT(!res.Root);
- }
-
- Y_UNIT_TEST(CreateTableAsSelectWithTypes) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32, primary key(a)) AS SELECT * FROM ts;");
- UNIT_ASSERT(!res.Root);
- }
-
- Y_UNIT_TEST(CreateTableAsSelect) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a, b, primary key(a)) AS SELECT * FROM ts;");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((let world (Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a") '('"b"))) '('primarykey '('"a"))))))__"));
- }
- if (word == "Read!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Read! world (DataSource '"yt" '"plato") (MrTableConcat (Key '('table (String '"ts")))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}, {TString("Read!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Read!"]);
- }
-
- Y_UNIT_TEST(CreateTableAsSelectOnlyPrimary) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (primary key(a)) AS SELECT * FROM ts;");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((let world (Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '()) '('primarykey '('"a"))))))__"));
- }
- if (word == "Read!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Read! world (DataSource '"yt" '"plato") (MrTableConcat (Key '('table (String '"ts")))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}, {TString("Read!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Read!"]);
- }
-
- Y_UNIT_TEST(CreateTableAsValuesFail) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a, primary key(a)) AS VALUES (1), (2);");
- UNIT_ASSERT(!res.Root);
- }
-
- Y_UNIT_TEST(CreateTableDuplicatedPkColumnsFail) {
- NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32 not null, primary key(a, a));");
- UNIT_ASSERT(!res.Root);
- }
-
- Y_UNIT_TEST(DeleteFromTableByKey) {
- NYql::TAstParseResult res = SqlToYql("delete from plato.Input where key = 200;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DeleteFromTable) {
- NYql::TAstParseResult res = SqlToYql("delete from plato.Input;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DeleteFromTableBatch) {
- NYql::TAstParseResult res = SqlToYql("batch delete from plato.Input;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete)"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('is_batch 'true)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DeleteFromTableOnValues) {
- NYql::TAstParseResult res = SqlToYql("delete from plato.Input on (key) values (1);",
- 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete_on)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DeleteFromTableOnSelect) {
- NYql::TAstParseResult res = SqlToYql(
- "delete from plato.Input on select key from plato.Input where value > 0;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete_on)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DeleteFromTableOnBatch) {
- NYql::TAstParseResult res = SqlToYql("batch delete from plato.Input on (key) values (1);",
- 10, "kikimr");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:6: Error: BATCH DELETE is unsupported with ON\n");
- }
-
- Y_UNIT_TEST(UpdateByValues) {
- NYql::TAstParseResult res = SqlToYql("update plato.Input set key = 777, value = 'cool' where key = 200;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)"));
- } else if (word == "AsStruct") {
- const bool isKey = line.find("key") != TString::npos;
- const bool isValue = line.find("value") != TString::npos;
- UNIT_ASSERT(isKey || isValue);
- if (isKey) {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("777")));
- } else if (isValue) {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("cool")));
- }
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}, {TString("AsStruct"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AsStruct"]);
- }
-
- Y_UNIT_TEST(UpdateByValuesBatch) {
- NYql::TAstParseResult res = SqlToYql("batch update plato.Input set key = 777, value = 'cool' where key = 200;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('is_batch 'true)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(UpdateByMultiValues) {
- NYql::TAstParseResult res = SqlToYql("update plato.Input set (key, value, subkey) = ('2','ddd',':') where key = 200;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)"));
- } else if (word == "AsStruct") {
- const bool isKey = line.find("key") != TString::npos;
- const bool isSubkey = line.find("subkey") != TString::npos;
- const bool isValue = line.find("value") != TString::npos;
- UNIT_ASSERT(isKey || isSubkey || isValue);
- if (isKey && !isSubkey) {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("2")));
- } else if (isSubkey) {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote(":")));
- } else if (isValue) {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("ddd")));
- }
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}, {TString("AsStruct"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AsStruct"]);
- }
-
- Y_UNIT_TEST(UpdateBySelect) {
- NYql::TAstParseResult res = SqlToYql("update plato.Input set (key, value, subkey) = (select key, value, subkey from plato.Input where key = 911) where key = 200;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- int lineIndex = 0;
- int writeLineIndex = -1;
- bool found = false;
-
- TVerifyLineFunc verifyLine = [&lineIndex, &writeLineIndex, &found](const TString& word, const TString& line) {
- if (word == "Write") {
- writeLineIndex = lineIndex;
- found = line.find("('mode 'update)") != TString::npos;
- } else if (word == "mode") {
- found |= lineIndex == writeLineIndex + 1 && line.find("('mode 'update)") != TString::npos;
- UNIT_ASSERT(found);
- }
-
- ++lineIndex;
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}, {TString("mode"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(UpdateSelfModifyAll) {
- NYql::TAstParseResult res = SqlToYql("update plato.Input set subkey = subkey + 's';", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)"));
- } else if (word == "AsStruct") {
- const bool isSubkey = line.find("subkey") != TString::npos;
- UNIT_ASSERT(isSubkey);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("subkey")));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("s")));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}, {TString("AsStruct"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AsStruct"]);
- }
-
- Y_UNIT_TEST(UpdateOnValues) {
- NYql::TAstParseResult res = SqlToYql("update plato.Input on (key, value) values (5, 'cool')", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update_on)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(UpdateOnSelect) {
- NYql::TAstParseResult res = SqlToYql(
- "update plato.Input on select key, value + 1 as value from plato.Input", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update_on)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(UpdateOnBatch) {
- NYql::TAstParseResult res = SqlToYql("batch update plato.Input on (key, value) values (5, 'cool')", 10, "kikimr");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:6: Error: BATCH UPDATE is unsupported with ON\n");
- }
-
- Y_UNIT_TEST(UnionAllTest) {
- NYql::TAstParseResult res = SqlToYql("PRAGMA DisableEmitUnionMerge; SELECT key FROM plato.Input UNION ALL select subkey FROM plato.Input;");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("UnionAll"), 0}};
- VerifyProgram(res, elementStat, {});
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["UnionAll"]);
- }
-
- Y_UNIT_TEST(UnionAllMergeTest) {
- NYql::TAstParseResult res = SqlToYql("PRAGMA EmitUnionMerge; SELECT key FROM plato.Input UNION ALL select subkey FROM plato.Input;");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("UnionMerge"), 0}};
- VerifyProgram(res, elementStat, {});
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["UnionMerge"]);
- }
-
- Y_UNIT_TEST(UnionTest) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input UNION select subkey FROM plato.Input;");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("Union"), 0}};
- VerifyProgram(res, elementStat, {});
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Union"]);
- }
-
- Y_UNIT_TEST(UnionAggregationTest) {
- NYql::TAstParseResult res = SqlToYql(R"(
- PRAGMA DisableEmitUnionMerge;
- SELECT 1
- UNION ALL
- SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
- UNION
- SELECT 1 UNION SELECT 1 UNION SELECT 1 UNION SELECT 1
- UNION ALL
- SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1;
- )");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("Union"), 0}, {TString("UnionAll"), 0}};
- VerifyProgram(res, elementStat, {});
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["UnionAll"]);
- UNIT_ASSERT_VALUES_EQUAL(3, elementStat["Union"]);
- }
-
- Y_UNIT_TEST(UnionMergeAggregationTest) {
- NYql::TAstParseResult res = SqlToYql(R"(
- PRAGMA EmitUnionMerge;
- SELECT 1
- UNION ALL
- SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
- UNION
- SELECT 1 UNION SELECT 1 UNION SELECT 1 UNION SELECT 1
- UNION ALL
- SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1;
- )");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("Union"), 0}, {TString("UnionMerge"), 0}};
- VerifyProgram(res, elementStat, {});
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["UnionMerge"]);
- UNIT_ASSERT_VALUES_EQUAL(3, elementStat["Union"]);
- }
-
- Y_UNIT_TEST(DeclareDecimalParameter) {
- NYql::TAstParseResult res = SqlToYql("declare $value as Decimal(22,9); select $value as cnt;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SimpleGroupBy) {
- NYql::TAstParseResult res = SqlToYql("select count(1),z from plato.Input group by key as z order by z;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(EmptyColumnName0) {
- /// Now it's parsed well and error occur on validate step like "4:31:Empty struct member name is not allowed" in "4:31:Function: AddMember"
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output (``, list1) values (0, AsList(0, 1, 2));");
- /// Verify that parsed well without crash
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(KikimrRollback) {
- NYql::TAstParseResult res = SqlToYql("use plato; select * from Input; rollback;", 10, "kikimr");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("rollback"), 0}};
- VerifyProgram(res, elementStat);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["rollback"]);
- }
-
- Y_UNIT_TEST(PragmaFile) {
- NYql::TAstParseResult res = SqlToYql(R"(pragma file("HW", "sbr:181041334");)");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString(R"((let world (Configure! world (DataSource '"config") '"AddFileByUrl" '"HW" '"sbr:181041334")))"), 0}};
- VerifyProgram(res, elementStat);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat.cbegin()->second);
- }
-
- Y_UNIT_TEST(DoNotCrashOnNamedInFilter) {
- NYql::TAstParseResult res = SqlToYql("USE plato; $all = ($table_name) -> { return true; }; SELECT * FROM FILTER(Input, $all)");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(PragmasFileAndUdfOrder) {
- NYql::TAstParseResult res = SqlToYql(R"(
- PRAGMA file("libvideoplayers_udf.so", "https://proxy.sandbox.yandex-team.ru/235185290");
- PRAGMA udf("libvideoplayers_udf.so");
- )");
- UNIT_ASSERT(res.Root);
-
- const auto programm = GetPrettyPrint(res);
- const auto file = programm.find("AddFileByUrl");
- const auto udfs = programm.find("ImportUdfs");
- UNIT_ASSERT(file < udfs);
- }
-
- Y_UNIT_TEST(ProcessUserType) {
- NYql::TAstParseResult res = SqlToYql("process plato.Input using Kikimr::PushData(TableRows());", 1, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Kikimr.PushData") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("TupleType"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("TypeOf"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Kikimr.PushData"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Kikimr.PushData"]);
- }
-
- Y_UNIT_TEST(ProcessUserTypeAuth) {
- NYql::TAstParseResult res = SqlToYql("process plato.Input using YDB::PushData(TableRows(), AsTuple('oauth', SecureParam('api:oauth')));", 1, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "YDB.PushData") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("TupleType"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("TypeOf"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("api:oauth"));
- }
- };
-
- TWordCountHive elementStat = {{TString("YDB.PushData"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["YDB.PushData"]);
- }
-
- Y_UNIT_TEST(SelectStreamRtmr) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato; INSERT INTO Output SELECT STREAM key FROM Input;",
- 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(res.Root);
-
- res = SqlToYql(
- "USE plato; INSERT INTO Output SELECT key FROM Input;",
- 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectStreamRtmrJoinWithYt) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato; INSERT INTO Output SELECT STREAM key FROM Input LEFT JOIN hahn.ttt as t ON Input.key = t.Name;",
- 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SelectStreamNonRtmr) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato; INSERT INTO Output SELECT STREAM key FROM Input;",
- 10);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:31: Error: SELECT STREAM is unsupported for non-streaming sources\n");
- }
-
- Y_UNIT_TEST(GroupByHopRtmr) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato; INSERT INTO Output SELECT key, SUM(value) AS value FROM Input
- GROUP BY key, HOP(subkey, "PT10S", "PT30S", "PT20S");
- )", 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(GroupByHopRtmrSubquery) {
- // 'use plato' intentially avoided
- NYql::TAstParseResult res = SqlToYql(R"(
- SELECT COUNT(*) AS value FROM (SELECT * FROM plato.Input)
- GROUP BY HOP(Data, "PT10S", "PT30S", "PT20S")
- )", 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(GroupByHopRtmrSubqueryBinding) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- $q = SELECT * FROM Input;
- INSERT INTO Output SELECT STREAM * FROM (
- SELECT COUNT(*) AS value FROM $q
- GROUP BY HOP(Data, "PT10S", "PT30S", "PT20S")
- );
- )", 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(GroupByNoHopRtmr) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato; INSERT INTO Output SELECT STREAM key, SUM(value) AS value FROM Input
- GROUP BY key;
- )", 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:22: Error: Streaming group by query must have a hopping window specification.\n");
- }
-
- Y_UNIT_TEST(KikimrInserts) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- INSERT INTO Output SELECT key, value FROM Input;
- INSERT OR ABORT INTO Output SELECT key, value FROM Input;
- INSERT OR IGNORE INTO Output SELECT key, value FROM Input;
- INSERT OR REVERT INTO Output SELECT key, value FROM Input;
- )", 10, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(WarnMissingIsBeforeNotNull) {
- NYql::TAstParseResult res = SqlToYql("select 1 NOT NULL");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Warning: Missing IS keyword before NOT NULL, code: 4507\n");
- }
-
- Y_UNIT_TEST(Subqueries) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- $sq1 = (SELECT * FROM plato.Input);
-
- $sq2 = SELECT * FROM plato.Input;
-
- $squ1 = (
- SELECT * FROM plato.Input
- UNION ALL
- SELECT * FROM plato.Input
- );
-
- $squ2 =
- SELECT * FROM plato.Input
- UNION ALL
- SELECT * FROM plato.Input;
-
- $squ3 = (
- (SELECT * FROM plato.Input)
- UNION ALL
- (SELECT * FROM plato.Input)
- );
-
- SELECT * FROM $sq1;
- SELECT * FROM $sq2;
- SELECT * FROM $squ1;
- SELECT * FROM $squ2;
- SELECT * FROM $squ3;
- )");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(SubqueriesJoin) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
-
- $left = SELECT * FROM plato.Input1 WHERE value != "BadValue";
- $right = SELECT * FROM plato.Input2;
-
- SELECT * FROM $left AS l
- JOIN $right AS r
- ON l.key == r.key;
- )");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(AnyInBackticksAsTableName) {
- NYql::TAstParseResult res = SqlToYql("use plato; select * from `any`;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(AnyJoinForTableAndSubQuery) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
-
- $r = SELECT * FROM plato.Input2;
-
- SELECT * FROM ANY plato.Input1 AS l
- LEFT JOIN ANY $r AS r
- USING (key);
- )");
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "EquiJoin") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('left 'any)"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('right 'any)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("left"), 0}, {TString("right"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["left"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["right"]);
- }
-
- Y_UNIT_TEST(AnyJoinForTableAndTableSource) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
-
- $r = AsList(
- AsStruct("aaa" as key, "bbb" as subkey, "ccc" as value)
- );
-
- SELECT * FROM ANY plato.Input1 AS l
- LEFT JOIN ANY AS_TABLE($r) AS r
- USING (key);
- )");
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "EquiJoin") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('left 'any)"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('right 'any)"));
- }
- };
-
- TWordCountHive elementStat = {{TString("left"), 0}, {TString("right"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["left"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["right"]);
- }
-
- Y_UNIT_TEST(AnyJoinNested) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
-
- FROM ANY Input1 as a
- JOIN Input2 as b ON a.key = b.key
- LEFT JOIN ANY Input3 as c ON a.key = c.key
- RIGHT JOIN ANY Input4 as d ON d.key = b.key
- CROSS JOIN Input5
- SELECT *;
- )");
-
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("left"), 0}, {TString("right"), 0}};
- VerifyProgram(res, elementStat);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["left"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["right"]);
- }
-
- Y_UNIT_TEST(InlineAction) {
- NYql::TAstParseResult res = SqlToYql(
- "do begin\n"
- " select 1\n"
- "; end do\n");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "");
- }
-
- Y_UNIT_TEST(FlattenByCorrelationName) {
- UNIT_ASSERT(SqlToYql("select * from plato.Input as t flatten by t.x").IsOk());
- UNIT_ASSERT(SqlToYql("select * from plato.Input as t flatten by t -- same as flatten by t.t").IsOk());
- }
-
- Y_UNIT_TEST(DiscoveryMode) {
- UNIT_ASSERT(SqlToYqlWithMode("insert into plato.Output select * from plato.Input", NSQLTranslation::ESqlMode::DISCOVERY).IsOk());
- UNIT_ASSERT(SqlToYqlWithMode("select * from plato.concat(Input1, Input2)", NSQLTranslation::ESqlMode::DISCOVERY).IsOk());
- UNIT_ASSERT(SqlToYqlWithMode("select * from plato.each(AsList(\"Input1\", \"Input2\"))", NSQLTranslation::ESqlMode::DISCOVERY).IsOk());
- }
-
- Y_UNIT_TEST(CubeWithAutoGeneratedLikeColumnName) {
- UNIT_ASSERT(SqlToYql("select key,subkey,group from plato.Input group by cube(key,subkey,group)").IsOk());
- }
-
- Y_UNIT_TEST(CubeWithAutoGeneratedLikeAlias) {
- UNIT_ASSERT(SqlToYql("select key,subkey,group from plato.Input group by cube(key,subkey,value as group)").IsOk());
- }
-
- Y_UNIT_TEST(FilterCanBeUsedAsColumnIdOrBind) {
- UNIT_ASSERT(SqlToYql("select filter from plato.Input").IsOk());
- UNIT_ASSERT(SqlToYql("select 1 as filter").IsOk());
- UNIT_ASSERT(SqlToYql("$filter = 1; select $filter").IsOk());
- }
-
- Y_UNIT_TEST(DuplicateSemicolonsAreAllowedBetweenTopLevelStatements) {
- UNIT_ASSERT(SqlToYql(";;select 1; ; select 2;/*comment*/;select 3;;--comment\n;select 4;;").IsOk());
- }
-
- Y_UNIT_TEST(DuplicateAndMissingTrailingSemicolonsAreAllowedBetweenActionStatements) {
- TString req =
- "define action $action($b,$c) as\n"
- " ;;$d = $b + $c;\n"
- " select $b;\n"
- " select $c;;\n"
- " select $d,\n"
- "end define;\n"
- "\n"
- "do $action(1,2);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(DuplicateAndMissingTrailingSemicolonsAreAllowedBetweenInlineActionStatements) {
- TString req =
- "do begin\n"
- " ;select 1,\n"
- "end do;\n"
- "evaluate for $i in AsList(1,2,3) do begin\n"
- " select $i;;\n"
- " select $i + $i;;\n"
- "end do;";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(DuplicateSemicolonsAreAllowedBetweenLambdaStatements) {
- TString req =
- "$x=1;\n"
- "$foo = ($a, $b)->{\n"
- " ;;$v = $a + $b;\n"
- " $bar = ($c) -> {; return $c << $x};;\n"
- " return $bar($v);;\n"
- "};\n"
- "select $foo(1,2);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(StringLiteralWithEscapedBackslash) {
- NYql::TAstParseResult res1 = SqlToYql(R"foo(SELECT 'a\\';)foo");
- NYql::TAstParseResult res2 = SqlToYql(R"foo(SELECT "a\\";)foo");
- UNIT_ASSERT(res1.Root);
- UNIT_ASSERT(res2.Root);
-
- TWordCountHive elementStat = {{TString("a\\"), 0}};
-
- VerifyProgram(res1, elementStat);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["a\\"]);
-
- VerifyProgram(res2, elementStat);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["a\\"]);
- }
-
- Y_UNIT_TEST(StringMultiLineLiteralWithEscapes) {
- UNIT_ASSERT(SqlToYql("SELECT @@@foo@@@@bar@@@").IsOk());
- UNIT_ASSERT(SqlToYql("SELECT @@@@@@@@@").IsOk());
- }
-
- Y_UNIT_TEST(StringMultiLineLiteralConsequitiveAt) {
- UNIT_ASSERT(!SqlToYql("SELECT @").IsOk());
- UNIT_ASSERT(!SqlToYql("SELECT @@").IsOk());
- UNIT_ASSERT(!SqlToYql("SELECT @@@").IsOk());
- UNIT_ASSERT( SqlToYql("SELECT @@@@").IsOk());
- UNIT_ASSERT( SqlToYql("SELECT @@@@@").IsOk());
-
- UNIT_ASSERT(!SqlToYql("SELECT @@@@@@").IsOk());
- UNIT_ASSERT(!SqlToYql("SELECT @@@@@@@").IsOk());
-
- UNIT_ASSERT( SqlToYql("SELECT @@@@@@@@").IsOk());
- UNIT_ASSERT( SqlToYql("SELECT @@@@@@@@@").IsOk());
- UNIT_ASSERT(!SqlToYql("SELECT @@@@@@@@@@").IsOk());
- }
-
- Y_UNIT_TEST(ConstnessForListDictSetCreate) {
- auto req = "$foo = ($x, $y) -> (\"aaaa\");\n"
- "\n"
- "select\n"
- " $foo(sum(key), ListCreate(String)),\n"
- " $foo(sum(key), DictCreate(String, String)),\n"
- " $foo(sum(key), SetCreate(String)),\n"
- "from (select 1 as key);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(CanUseEmptyTupleInWindowPartitionBy) {
- auto req = "select sum(key) over w\n"
- "from plato.Input\n"
- "window w as (partition compact by (), (subkey), (), value || value as dvalue);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(DenyAnsiOrderByLimitLegacyMode) {
- auto req = "pragma DisableAnsiOrderByLimitInUnionAll;\n"
- "use plato;\n"
- "\n"
- "select * from Input order by key limit 10\n"
- "union all\n"
- "select * from Input order by key limit 1;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: DisableAnsiOrderByLimitInUnionAll pragma is deprecated and no longer supported\n");
- }
-
- Y_UNIT_TEST(ReduceUsingUdfWithShortcutsWorks) {
- auto req = "use plato;\n"
- "\n"
- "$arg = 'foo';\n"
- "$func = XXX::YYY($arg);\n"
- "\n"
- "REDUCE Input ON key using $func(subkey);\n"
- "REDUCE Input ON key using $func(UUU::VVV(TableRow()));\n";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- req = "use plato;\n"
- "\n"
- "$arg = 'foo';\n"
- "$func = XXX::YYY($arg);\n"
- "\n"
- "REDUCE Input ON key using all $func(subkey);\n"
- "REDUCE Input ON key using all $func(UUU::VVV(TableRow()));";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(YsonDisableStrict) {
- UNIT_ASSERT(SqlToYql("pragma yson.DisableStrict = \"false\";").IsOk());
- UNIT_ASSERT(SqlToYql("pragma yson.DisableStrict;").IsOk());
- }
-
- Y_UNIT_TEST(YsonStrict) {
- UNIT_ASSERT(SqlToYql("pragma yson.Strict = \"false\";").IsOk());
- UNIT_ASSERT(SqlToYql("pragma yson.Strict;").IsOk());
- }
-
- Y_UNIT_TEST(JoinByTuple) {
- auto req = "use plato;\n"
- "\n"
- "select * from T1 as a\n"
- "join T2 as b\n"
- "on AsTuple(a.key, a.subkey) = AsTuple(b.key, b.subkey);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(JoinByStruct) {
- auto req = "use plato;\n"
- "\n"
- "select * from T1 as a\n"
- "join T2 as b\n"
- "on AsStruct(a.key as k, a.subkey as sk) = AsStruct(b.key as k, b.subkey as sk);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(JoinByUdf) {
- auto req = "use plato;\n"
- "\n"
- "select a.align\n"
- "from T1 as a\n"
- "join T2 as b\n"
- "on Yson::SerializeJsonEncodeUtf8(a.align)=b.align;";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(EscapedIdentifierAsLambdaArg) {
- auto req = "$f = ($`foo bar`, $x) -> { return $`foo bar` + $x; };\n"
- "\n"
- "select $f(1, 2);";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(lambda '(\"$foo bar\" \"$x\")";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarOnlyCallable) {
- auto req = "SELECT Udf(DateTime::FromString)('2022-01-01');";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType)))";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarTypeNoRun) {
- auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, 'foo' as TypeConfig)('2022-01-01');";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\")";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarRunNoType) {
- auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, Void() as RunConfig)('2022-01-01');";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"\" (Void))";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarFullTest) {
- auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, 'foo' as TypeConfig, Void() As RunConfig)('2022-01-01');";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\" (Void))";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarOtherRunConfigs) {
- auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, 'foo' as TypeConfig, '55' As RunConfig)('2022-01-01');";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\" (String '\"55\"))";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarOtherRunConfigs2) {
- auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, 'foo' as TypeConfig, AsTuple(32, 'no', AsStruct(1e-9 As SomeFloat)) As RunConfig)('2022-01-01');";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\" '((Int32 '\"32\") (String '\"no\") (AsStruct '('\"SomeFloat\" (Double '\"1e-9\")))))";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarOptional) {
- auto req = "SELECT Udf(DateTime::FromString, String?, Int32??, Tuple<Int32, Float>, \"foo\" as TypeConfig, Void() As RunConfig)(\"2022-01-01\");";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- const auto programm = GetPrettyPrint(res);
- auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (OptionalType (DataType 'String)) (OptionalType (OptionalType (DataType 'Int32))) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\" (Void))";
- UNIT_ASSERT(programm.find(expected) != TString::npos);
- }
-
- Y_UNIT_TEST(CompactionPolicyParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
- WITH ( COMPACTION_POLICY = "SomeCompactionPreset" );)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("compactionPolicy"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("SomeCompactionPreset"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AutoPartitioningBySizeParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
- WITH ( AUTO_PARTITIONING_BY_SIZE = ENABLED );)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("autoPartitioningBySize"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("ENABLED"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(UniformPartitionsParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
- WITH ( UNIFORM_PARTITIONS = 16 );)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("uniformPartitions"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("16"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DateTimeTtlParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, CreatedAt Timestamp, PRIMARY KEY (Key))
- WITH (TTL = Interval("P1D") On CreatedAt);)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("setTtlSettings"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiers"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("evictionDelay"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("86400000"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(IntTtlParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, CreatedAt Uint32, PRIMARY KEY (Key))
- WITH (TTL = Interval("P1D") On CreatedAt AS SECONDS);)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("setTtlSettings"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiers"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("evictionDelay"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("86400000"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("columnUnit"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("seconds"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(TtlTieringParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, CreatedAt Uint32, PRIMARY KEY (Key))
- WITH (TTL =
- Interval("P1D") TO EXTERNAL DATA SOURCE Tier1,
- Interval("P2D") TO EXTERNAL DATA SOURCE Tier2,
- Interval("P30D") DELETE
- On CreatedAt AS SECONDS);)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("setTtlSettings"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiers"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("evictionDelay"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("storageName"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Tier1"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Tier2"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("86400000"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("172800000"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("2592000000"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("columnUnit"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("seconds"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(TtlTieringWithOtherActionsParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- ALTER TABLE tableName
- ADD FAMILY cold (DATA = "rot"),
- SET TTL
- Interval("P1D") TO EXTERNAL DATA SOURCE Tier1,
- Interval("P2D") TO EXTERNAL DATA SOURCE Tier2,
- Interval("P30D") DELETE
- ON CreatedAt,
- ALTER COLUMN payload_v2 SET FAMILY cold,
- ALTER FAMILY default SET DATA "ssd"
- ;)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("addColumnFamilies"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("cold"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alterColumnFamilies"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("default"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("setTtlSettings"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiers"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("evictionDelay"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("storageName"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Tier1"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Tier2"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("86400000"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("172800000"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("2592000000"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(TieringParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
- WITH ( TIERING = 'my_tiering' );)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiering"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("my_tiering"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(StoreExternalBlobsParseCorrect) {
- NYql::TAstParseResult res = SqlToYql(
- R"( USE plato;
- CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
- WITH ( STORE_EXTERNAL_BLOBS = ENABLED );)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("storeExternalBlobs"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("ENABLED"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DefaultValueColumn2) {
- auto res = SqlToYql(R"( use plato;
- $lambda = () -> {
- RETURN CAST(RandomUuid(2) as String)
- };
-
- CREATE TABLE tableName (
- Key Uint32 DEFAULT RandomNumber(1),
- Value String DEFAULT $lambda,
- PRIMARY KEY (Key)
- );
- )");
-
- UNIT_ASSERT_C(res.Root, Err2Str(res));
-
- const auto program = GetPrettyPrint(res);
-
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("RandomNumber"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("RandomUuid"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("columnConstrains"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("columnConstrains"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("Write"));
-
-#if 0
- Cerr << program << Endl;
-#endif
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DefaultValueColumn3) {
- auto res = SqlToYql(R"( use plato;
-
- CREATE TABLE tableName (
- database_id Utf8,
- cloud_id Utf8,
- global_id Utf8 DEFAULT database_id || "=====",
- PRIMARY KEY (database_id)
- );
- )");
-
- UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:6:40: Error: Column reference \"database_id\" is not allowed in current scope\n");
- UNIT_ASSERT(!res.Root);
- }
-
- Y_UNIT_TEST(DefaultValueColumn) {
- auto res = SqlToYql(R"( use plato;
- CREATE TABLE tableName (
- Key Uint32 FAMILY cold DEFAULT 5,
- Value String FAMILY default DEFAULT "empty",
- PRIMARY KEY (Key),
- FAMILY default (
- DATA = "test",
- COMPRESSION = "lz4"
- ),
- FAMILY cold (
- DATA = "test",
- COMPRESSION = "off"
- )
- );
- )");
-
- UNIT_ASSERT_C(res.Root, Err2Str(res));
-
-#if 0
- const auto program = GetPrettyPrint(res);
- Cerr << program << Endl;
-#endif
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("default"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("columnConstrains"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("columnFamilies"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(ChangefeedParseCorrect) {
- auto res = SqlToYql(R"( USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (
- MODE = 'KEYS_ONLY',
- FORMAT = 'json',
- INITIAL_SCAN = TRUE,
- VIRTUAL_TIMESTAMPS = FALSE,
- BARRIERS_INTERVAL = Interval("PT1S"),
- RETENTION_PERIOD = Interval("P1D"),
- TOPIC_MIN_ACTIVE_PARTITIONS = 10,
- AWS_REGION = 'aws:region'
- )
- );
- )");
- UNIT_ASSERT_C(res.Root, Err2Str(res));
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("changefeed"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("mode"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("KEYS_ONLY"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("format"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("json"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("initial_scan"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("true"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("virtual_timestamps"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("false"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("barriers_interval"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("retention_period"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("topic_min_active_partitions"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("aws_region"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("aws:region"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CloneForAsTableWorksWithCube) {
- UNIT_ASSERT(SqlToYql("SELECT * FROM AS_TABLE([<|k1:1, k2:1|>]) GROUP BY CUBE(k1, k2);").IsOk());
- }
-
- Y_UNIT_TEST(WindowPartitionByColumnProperlyEscaped) {
- NYql::TAstParseResult res = SqlToYql("SELECT SUM(key) OVER w FROM plato.Input WINDOW w AS (PARTITION BY `column with space`);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "CalcOverWindow") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"column with space\""));
- }
- };
-
- TWordCountHive elementStat = { {TString("CalcOverWindow"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["CalcOverWindow"]);
- }
-
- Y_UNIT_TEST(WindowPartitionByExpressionWithoutAliasesAreAllowed) {
- NYql::TAstParseResult res = SqlToYql("SELECT SUM(key) OVER w FROM plato.Input as i WINDOW w AS (PARTITION BY ii.subkey);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "AddMember") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("AddMember row 'group_w_0 (SqlAccess 'struct (Member row '\"ii\")"));
- }
- if (word == "CalcOverWindow") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("CalcOverWindow core '('\"group_w_0\")"));
- }
- };
-
- TWordCountHive elementStat = { {TString("CalcOverWindow"), 0}, {TString("AddMember"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["CalcOverWindow"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AddMember"]);
- }
-
- Y_UNIT_TEST(PqReadByAfterUse) {
- ExpectFailWithError("use plato; pragma PqReadBy='plato2';",
- "<main>:1:28: Error: Cluster in PqReadPqBy pragma differs from cluster specified in USE statement: plato2 != plato\n");
-
- UNIT_ASSERT(SqlToYql("pragma PqReadBy='plato2';").IsOk());
- UNIT_ASSERT(SqlToYql("pragma PqReadBy='plato2'; use plato;").IsOk());
- UNIT_ASSERT(SqlToYql("$x='plato'; use rtmr:$x; pragma PqReadBy='plato2';").IsOk());
- UNIT_ASSERT(SqlToYql("use plato; pragma PqReadBy='dq';").IsOk());
- }
-
- Y_UNIT_TEST(MrObject) {
- NYql::TAstParseResult res = SqlToYql(
- "declare $path as String;\n"
- "select * from plato.object($path, `format`, \"comp\" || \"ression\" as compression, 1 as bar) with schema (Int32 as y, String as x)"
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "MrObject") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((MrObject (EvaluateAtom "$path") '"format" '('('"compression" (Concat (String '"comp") (String '"ression"))) '('"bar" (Int32 '"1")))))__"));
- } else if (word == "userschema") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__('('('"userschema" (StructType '('"y" (DataType 'Int32)) '('"x" (DataType 'String))) '('"y" '"x"))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("MrObject"), 0}, {TString("userschema"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["MrObject"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["userschema"]);
- }
-
- Y_UNIT_TEST(TableBindings) {
- NSQLTranslation::TTranslationSettings settings = GetSettingsWithS3Binding("foo");
- NYql::TAstParseResult res = SqlToYqlWithSettings(
- "select * from bindings.foo",
- settings
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "MrObject") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((MrTableConcat (Key '('table (String '"path")))) (Void) '('('"bar" '"1") '('"compression" '"ccompression") '('"format" '"format") '('"partitionedby" '"key" '"subkey") '('"userschema" (SqlTypeFromYson)__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("MrTableConcat"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["MrTableConcat"]);
-
- settings.DefaultCluster = "plato";
- settings.BindingsMode = NSQLTranslation::EBindingsMode::DISABLED;
- res = SqlToYqlWithSettings(
- "select * from bindings.foo",
- settings
- );
- UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:1:15: Error: Please remove 'bindings.' from your query, the support for this syntax has ended, code: 4601\n");
- UNIT_ASSERT(!res.Root);
-
- settings.BindingsMode = NSQLTranslation::EBindingsMode::DROP;
- res = SqlToYqlWithSettings(
- "select * from bindings.foo",
- settings
- );
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine2 = [](const TString& word, const TString& line) {
- if (word == "MrTableConcat") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((MrTableConcat (Key '('table (String '"foo")))) (Void) '())))__"));
- }
- };
-
- TWordCountHive elementStat2 = {{TString("MrTableConcat"), 0}};
- VerifyProgram(res, elementStat2, verifyLine2);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat2["MrTableConcat"]);
-
- settings.BindingsMode = NSQLTranslation::EBindingsMode::DROP_WITH_WARNING;
- res = SqlToYqlWithSettings(
- "select * from bindings.foo",
- settings
- );
- UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:1:15: Warning: Please remove 'bindings.' from your query, the support for this syntax will be dropped soon, code: 4538\n");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat3 = {{TString("MrTableConcat"), 0}};
- VerifyProgram(res, elementStat3, verifyLine2);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat3["MrTableConcat"]);
- }
-
- Y_UNIT_TEST(TableBindingsWithInsert) {
- NSQLTranslation::TTranslationSettings settings = GetSettingsWithS3Binding("foo");
- NYql::TAstParseResult res = SqlToYqlWithSettings(
- "insert into bindings.foo with truncate (x, y) values (1, 2);",
- settings
- );
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('table (String '"path"))) values '('('"bar" '"1") '('"compression" '"ccompression") '('"format" '"format") '('"partitionedby" '"key" '"subkey") '('"userschema" (SqlTypeFromYson)__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
-
- settings.DefaultCluster = "plato";
- settings.BindingsMode = NSQLTranslation::EBindingsMode::DISABLED;
- res = SqlToYqlWithSettings(
- "insert into bindings.foo with truncate (x, y) values (1, 2);",
- settings
- );
- UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:1:13: Error: Please remove 'bindings.' from your query, the support for this syntax has ended, code: 4601\n");
- UNIT_ASSERT(!res.Root);
-
- settings.BindingsMode = NSQLTranslation::EBindingsMode::DROP;
- res = SqlToYqlWithSettings(
- "insert into bindings.foo with truncate (x, y) values (1, 2);",
- settings
- );
- UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine2 = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- //UNIT_ASSERT_VALUES_EQUAL(line, "");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__((Write! world sink (Key '('table (String '"foo"))) values '('('mode 'renew)))__"));
- }
- };
-
- TWordCountHive elementStat2 = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat2, verifyLine2);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat2["Write!"]);
-
- settings.BindingsMode = NSQLTranslation::EBindingsMode::DROP_WITH_WARNING;
- res = SqlToYqlWithSettings(
- "insert into bindings.foo with truncate (x, y) values (1, 2);",
- settings
- );
- UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:1:13: Warning: Please remove 'bindings.' from your query, the support for this syntax will be dropped soon, code: 4538\n");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat3 = {{TString("Write!"), 0}};
- VerifyProgram(res, elementStat3, verifyLine2);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat3["Write!"]);
- }
-
- Y_UNIT_TEST(TrailingCommaInWithout) {
- UNIT_ASSERT(SqlToYql("SELECT * WITHOUT stream, FROM plato.Input").IsOk());
- UNIT_ASSERT(SqlToYql("SELECT a.* WITHOUT a.intersect, FROM plato.Input AS a").IsOk());
- UNIT_ASSERT(SqlToYql("SELECT a.* WITHOUT col1, col2, a.col3, FROM plato.Input AS a").IsOk());
- }
-
- Y_UNIT_TEST(NoStackOverflowOnBigCaseStatement) {
- TStringBuilder req;
- req << "select case 1 + 123";
- for (size_t i = 0; i < 20000; ++i) {
- req << " when " << i << " then " << i + 1;
- }
- req << " else 100500 end;";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(CollectPreaggregatedInListLiteral) {
- UNIT_ASSERT(SqlToYql("SELECT [COUNT(DISTINCT a+b)] FROM plato.Input").IsOk());
- }
-
- Y_UNIT_TEST(SmartParenInGroupByClause) {
- UNIT_ASSERT(SqlToYql("SELECT * FROM plato.Input GROUP BY (k, v)").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableRenameToIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table RENAME TO moved").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableAddDropColumnIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table ADD COLUMN addc uint64, DROP COLUMN dropc, ADD addagain uint64").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableSetTTLIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table SET (TTL = Interval(\"PT3H\") ON column)").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table SET (TTL = Interval(\"PT3H\") ON column AS SECONDS)").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableSetTieringIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table SET (TIERING = 'my_tiering')").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableAddChangefeedIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table ADD CHANGEFEED feed WITH (MODE = 'UPDATES', FORMAT = 'json')").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableAlterChangefeedIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table ALTER CHANGEFEED feed DISABLE").IsOk());
- ExpectFailWithError("USE plato; ALTER TABLE table ALTER CHANGEFEED feed SET (FORMAT = 'proto');",
- "<main>:1:57: Error: FORMAT alter is not supported\n");
- }
-
- Y_UNIT_TEST(AlterTableDropChangefeedIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table DROP CHANGEFEED feed").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableSetPartitioningIsCorrect) {
- UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table SET (AUTO_PARTITIONING_BY_SIZE = DISABLED)").IsOk());
- }
-
- Y_UNIT_TEST(AlterTableAddIndexWithIsNotSupported) {
- ExpectFailWithError("USE plato; ALTER TABLE table ADD INDEX idx GLOBAL ON (col) WITH (a=b)",
- "<main>:1:40: Error: with: alternative is not implemented yet: \n");
- }
-
- Y_UNIT_TEST(AlterTableAddIndexLocalIsNotSupported) {
- ExpectFailWithError("USE plato; ALTER TABLE table ADD INDEX idx LOCAL ON (col)",
- "<main>:1:40: Error: local: alternative is not implemented yet: \n");
- }
-
- Y_UNIT_TEST(CreateTableAddIndexVector) {
- const auto result = SqlToYql(R"(USE plato;
- CREATE TABLE table (
- pk INT32 NOT NULL,
- col String,
- INDEX idx GLOBAL USING vector_kmeans_tree
- ON (col) COVER (col)
- WITH (distance=cosine, vector_type=float, vector_dimension=1024,),
- PRIMARY KEY (pk))
- )");
- UNIT_ASSERT_C(result.IsOk(), result.Issues.ToString());
- }
-
- Y_UNIT_TEST(AlterTableAddIndexVector) {
- const auto result = SqlToYql(R"(USE plato;
- ALTER TABLE table ADD INDEX idx
- GLOBAL USING vector_kmeans_tree
- ON (col) COVER (col)
- WITH (distance=cosine, vector_type="float", vector_dimension=1024)
- )");
- UNIT_ASSERT_C(result.IsOk(), result.Issues.ToString());
- }
-
- Y_UNIT_TEST(AlterTableAddIndexUnknownSubtype) {
- ExpectFailWithError("USE plato; ALTER TABLE table ADD INDEX idx GLOBAL USING unknown ON (col)",
- "<main>:1:57: Error: UNKNOWN index subtype is not supported\n");
- }
-
- Y_UNIT_TEST(AlterTableAddIndexMissedParameter) {
- ExpectFailWithError(R"(USE plato;
- ALTER TABLE table ADD INDEX idx
- GLOBAL USING vector_kmeans_tree
- ON (col)
- WITH (distance=cosine, vector_type=float)
- )",
- "<main>:5:52: Error: vector_dimension should be set\n");
- }
-
- Y_UNIT_TEST(AlterTableAlterIndexSetPartitioningIsCorrect) {
- const auto result = SqlToYql("USE plato; ALTER TABLE table ALTER INDEX index SET AUTO_PARTITIONING_MIN_PARTITIONS_COUNT 10");
- UNIT_ASSERT_C(result.IsOk(), result.Issues.ToString());
- }
-
- Y_UNIT_TEST(AlterTableAlterIndexSetMultiplePartitioningSettings) {
- const auto result = SqlToYql("USE plato; ALTER TABLE table ALTER INDEX index SET "
- "(AUTO_PARTITIONING_BY_LOAD = ENABLED, AUTO_PARTITIONING_MIN_PARTITIONS_COUNT = 10)"
- );
- UNIT_ASSERT_C(result.IsOk(), result.Issues.ToString());
- }
-
- Y_UNIT_TEST(AlterTableAlterIndexResetPartitioningIsNotSupported) {
- ExpectFailWithError("USE plato; ALTER TABLE table ALTER INDEX index RESET (AUTO_PARTITIONING_MIN_PARTITIONS_COUNT)",
- "<main>:1:55: Error: AUTO_PARTITIONING_MIN_PARTITIONS_COUNT reset is not supported\n"
- );
- }
-
- Y_UNIT_TEST(AlterTableAlterColumnDropNotNullAstCorrect) {
- auto reqSetNull = SqlToYql(R"(
- USE plato;
- CREATE TABLE tableName (
- id Uint32,
- val Uint32 NOT NULL,
- PRIMARY KEY (id)
- );
-
- COMMIT;
- ALTER TABLE tableName ALTER COLUMN val DROP NOT NULL;
- )");
-
- UNIT_ASSERT(reqSetNull.IsOk());
- UNIT_ASSERT(reqSetNull.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- Y_UNUSED(word);
-
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(
- R"(let world (Write! world sink (Key '('tablescheme (String '"tableName"))) (Void) '('('mode 'alter) '('actions '('('alterColumns '('('"val" '('changeColumnConstraints '('('drop_not_null)))))))))))"
- ));
- };
-
- TWordCountHive elementStat({TString("\'mode \'alter")});
- VerifyProgram(reqSetNull, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["\'mode \'alter"]);
- }
-
- Y_UNIT_TEST(AlterSequence) {
- UNIT_ASSERT(SqlToYql(R"(
- USE plato;
- ALTER SEQUENCE sequence START WITH 10 INCREMENT 2 RESTART WITH 5;
- )").IsOk());
- UNIT_ASSERT(SqlToYql(R"(
- USE plato;
- ALTER SEQUENCE sequence INCREMENT 2;
- )").IsOk());
- UNIT_ASSERT(SqlToYql(R"(
- USE plato;
- ALTER SEQUENCE sequence INCREMENT 2 START 1000;
- )").IsOk());
- UNIT_ASSERT(SqlToYql(R"(
- USE plato;
- ALTER SEQUENCE sequence RESTART START 1000;
- )").IsOk());
- UNIT_ASSERT(SqlToYql(R"(
- USE plato;
- ALTER SEQUENCE IF EXISTS sequence INCREMENT 1000 START 100 RESTART;
- )").IsOk());
- UNIT_ASSERT(SqlToYql(R"(
- USE plato;
- ALTER SEQUENCE IF EXISTS sequence RESTART 1000 START WITH 100 INCREMENT BY 7;
- )").IsOk());
- }
-
- Y_UNIT_TEST(AlterSequenceIncorrect) {
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence START WITH 10 INCREMENT 2 RESTART WITH 5 RESTART;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:75: Error: Restart value defined more than once\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence START WITH 10 INCREMENT 2 START 100 RESTART WITH 5;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:60: Error: Start value defined more than once\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence INCREMENT BY 7 START WITH 10 INCREMENT 2 RESTART WITH 5 RESTART;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:62: Error: Increment defined more than once\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 100 START WITH 10 INCREMENT 2 RESTART WITH 5;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:77: Error: Restart value defined more than once\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1234234543563435151456 START WITH 10 INCREMENT 2;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Error: Failed to parse number from string: 1234234543563435151456, number limit overflow\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1 START WITH 9223372036854775817 INCREMENT 4;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Start value: 9223372036854775817 cannot be greater than max value: 9223372036854775807\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 9223372036854775827 START WITH 5 INCREMENT 4;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Restart value: 9223372036854775827 cannot be greater than max value: 9223372036854775807\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1 START WITH 4 INCREMENT 0;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Increment must not be zero\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 0 START WITH 4 INCREMENT 1;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Restart value: 0 cannot be less than min value: 1\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1 START WITH 0 INCREMENT 1;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Start value: 0 cannot be less than min value: 1\n");
- }
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1 START WITH 1 INCREMENT 9223372036854775837;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Increment: 9223372036854775837 cannot be greater than max value: 9223372036854775807\n");
- }
- }
-
- Y_UNIT_TEST(AlterSequenceCorrect) {
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence START WITH 10 INCREMENT 2 RESTART WITH 5;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("sequence"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter"));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("alter_if_exists"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("start"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("increment"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("restart"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE IF EXISTS sequence INCREMENT 2 RESTART;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("sequence"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter_if_exists"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("increment"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("restart"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- {
- NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE IF EXISTS sequence START 10 INCREMENT BY 2;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("sequence"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter_if_exists"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("start"));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("restart"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("increment"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
- }
-
- Y_UNIT_TEST(ShowCreateTable) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- SHOW CREATE TABLE user;
- )");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Read") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("showCreateTable"));
- }
- };
-
- TWordCountHive elementStat = {{TString("Read"), 0}, {TString("showCreateTable"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Read"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["showCreateTable"]);
- }
-
- Y_UNIT_TEST(OptionalAliases) {
- UNIT_ASSERT(SqlToYql("USE plato; SELECT foo FROM (SELECT key foo FROM Input);").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT a.x FROM Input1 a JOIN Input2 b ON a.key = b.key;").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; SELECT a.x FROM (VALUES (1,2), (3,4)) a(x,key) JOIN Input b ON a.key = b.key;").IsOk());
- }
-
- Y_UNIT_TEST(TableNameConstness) {
- UNIT_ASSERT(SqlToYql("USE plato; $path = 'foo'; SELECT TableName($path), count(*) FROM Input;").IsOk());
- UNIT_ASSERT(SqlToYql("$path = 'foo'; SELECT TableName($path, 'yt'), count(*) FROM plato.Input;").IsOk());
- ExpectFailWithError("USE plato; SELECT TableName(), count(*) FROM plato.Input;",
- "<main>:1:19: Error: Expression has to be an aggregation function or key column, because aggregation is used elsewhere in this subquery\n");
- }
-
- Y_UNIT_TEST(UseShouldWorkAsColumnName) {
- UNIT_ASSERT(SqlToYql("select use from (select 1 as use);").IsOk());
- }
-
- Y_UNIT_TEST(TrueFalseWorkAfterDollar) {
- UNIT_ASSERT(SqlToYql("$ true = false; SELECT $ true or false;").IsOk());
- UNIT_ASSERT(SqlToYql("$False = 0; SELECT $False;").IsOk());
- }
-
- Y_UNIT_TEST(WithSchemaEquals) {
- UNIT_ASSERT(SqlToYql("select * from plato.T with schema Struct<a:Int32, b:String>;").IsOk());
- UNIT_ASSERT(SqlToYql("select * from plato.T with columns = Struct<a:Int32, b:String>;").IsOk());
- }
-
- Y_UNIT_TEST(WithNonStructSchemaS3) {
- NSQLTranslation::TTranslationSettings settings;
- settings.ClusterMapping["s3bucket"] = NYql::S3ProviderName;
- UNIT_ASSERT(SqlToYqlWithSettings("select * from s3bucket.`foo` with schema (col1 Int32, String as col2, Int64 as col3);", settings).IsOk());
- }
-
- Y_UNIT_TEST(AllowNestedTuplesInGroupBy) {
- NYql::TAstParseResult res = SqlToYql("select count(*) from plato.Input group by 1 + (x, y, z);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Aggregate core '('\"group0\")"));
- };
-
- TWordCountHive elementStat({"Aggregate"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["Aggregate"] == 1);
- }
-
- Y_UNIT_TEST(AllowGroupByWithParens) {
- NYql::TAstParseResult res = SqlToYql("select count(*) from plato.Input group by (x, y as alias1, z);");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Aggregate core '('\"x\" '\"alias1\" '\"z\")"));
- };
-
- TWordCountHive elementStat({"Aggregate"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["Aggregate"] == 1);
- }
-
- Y_UNIT_TEST(CreateAsyncReplicationParseCorrect) {
- auto req = R"(
- USE plato;
- CREATE ASYNC REPLICATION MyReplication
- FOR table1 AS table2, table3 AS table4
- WITH (
- CONNECTION_STRING = "grpc://localhost:2135/?database=/MyDatabase",
- ENDPOINT = "localhost:2135",
- DATABASE = "/MyDatabase"
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("MyReplication"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("create"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table1"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table2"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table3"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table4"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("connection_string"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("grpc://localhost:2135/?database=/MyDatabase"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("endpoint"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("localhost:2135"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("database"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("/MyDatabase"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateAsyncReplicationUnsupportedSettings) {
- auto reqTpl = R"(
- USE plato;
- CREATE ASYNC REPLICATION MyReplication
- FOR table1 AS table2, table3 AS table4
- WITH (
- %s = "%s"
- )
- )";
-
- auto settings = THashMap<TString, TString>{
- {"STATE", "DONE"},
- {"FAILOVER_MODE", "FORCE"},
- };
-
- for (const auto& [k, v] : settings) {
- auto req = Sprintf(reqTpl, k.c_str(), v.c_str());
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), Sprintf("<main>:6:%zu: Error: %s is not supported in CREATE\n", 20 + k.size(), k.c_str()));
- }
- }
-
- Y_UNIT_TEST(AsyncReplicationInvalidCommitInterval) {
- auto req = R"(
- USE plato;
- CREATE ASYNC REPLICATION MyReplication
- FOR table1 AS table2, table3 AS table4
- WITH (
- COMMIT_INTERVAL = "FOO"
- );
- )";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:6:35: Error: Literal of Interval type is expected for COMMIT_INTERVAL\n");
- }
-
- Y_UNIT_TEST(AlterAsyncReplicationParseCorrect) {
- auto req = R"(
- USE plato;
- ALTER ASYNC REPLICATION MyReplication
- SET (
- STATE = "DONE",
- FAILOVER_MODE = "FORCE"
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("MyReplication"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("state"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("DONE"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("failover_mode"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("FORCE"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AlterAsyncReplicationSettings) {
- auto reqTpl = R"(
- USE plato;
- ALTER ASYNC REPLICATION MyReplication
- SET (
- %s = "%s"
- )
- )";
-
- auto settings = THashMap<TString, TString>{
- {"connection_string", "grpc://localhost:2135/?database=/MyDatabase"},
- {"endpoint", "localhost:2135"},
- {"database", "/MyDatabase"},
- {"token", "foo"},
- {"token_secret_name", "foo_secret_name"},
- {"user", "user"},
- {"password", "bar"},
- {"password_secret_name", "bar_secret_name"},
- };
-
- for (const auto& [k, v] : settings) {
- auto req = Sprintf(reqTpl, k.c_str(), v.c_str());
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&k, &v](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("MyReplication"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(k));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(v));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
- }
-
- Y_UNIT_TEST(AlterAsyncReplicationUnsupportedSettings) {
- {
- auto req = R"(
- USE plato;
- ALTER ASYNC REPLICATION MyReplication SET (CONSISTENCY_LEVEL = "GLOBAL");
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:80: Error: CONSISTENCY_LEVEL is not supported in ALTER\n");
- }
- {
- auto req = R"(
- USE plato;
- ALTER ASYNC REPLICATION MyReplication SET (COMMIT_INTERVAL = Interval("PT10S"));
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:87: Error: COMMIT_INTERVAL is not supported in ALTER\n");
- }
- }
-
- Y_UNIT_TEST(AsyncReplicationInvalidSettings) {
- auto req = R"(
- USE plato;
- ALTER ASYNC REPLICATION MyReplication SET (FOO = "BAR");
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:62: Error: Unknown replication setting: FOO\n");
- }
-
- Y_UNIT_TEST(DropAsyncReplicationParseCorrect) {
- auto req = R"(
- USE plato;
- DROP ASYNC REPLICATION MyReplication;
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("MyReplication"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropAsyncReplicationCascade) {
- auto req = R"(
- USE plato;
- DROP ASYNC REPLICATION MyReplication CASCADE;
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropCascade"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(PragmaCompactGroupBy) {
- auto req = "PRAGMA CompactGroupBy; SELECT key, COUNT(*) FROM plato.Input GROUP BY key;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Aggregate") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('compact)"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Aggregate"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Aggregate"]);
- }
-
- Y_UNIT_TEST(PragmaDisableCompactGroupBy) {
- auto req = "PRAGMA DisableCompactGroupBy; SELECT key, COUNT(*) FROM plato.Input GROUP /*+ compact() */ BY key;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Aggregate") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'('compact)"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Aggregate"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Aggregate"]);
- }
-
- Y_UNIT_TEST(AutoSampleWorksWithNamedSubquery) {
- UNIT_ASSERT(SqlToYql("$src = select * from plato.Input; select * from $src sample 0.2").IsOk());
- }
-
- Y_UNIT_TEST(AutoSampleWorksWithSubquery) {
- UNIT_ASSERT(SqlToYql("select * from (select * from plato.Input) sample 0.2").IsOk());
- }
-
- Y_UNIT_TEST(CreateTableTrailingComma) {
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE tableName (Key Uint32, PRIMARY KEY (Key),);").IsOk());
- UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE tableName (Key Uint32,);").IsOk());
- }
-
- Y_UNIT_TEST(BetweenSymmetric) {
- UNIT_ASSERT(SqlToYql("select 3 between symmetric 5 and 4;").IsOk());
- UNIT_ASSERT(SqlToYql("select 3 between asymmetric 5 and 4;").IsOk());
- UNIT_ASSERT(SqlToYql("use plato; select key between symmetric and and and from Input;").IsOk());
- UNIT_ASSERT(SqlToYql("use plato; select key between and and and from Input;").IsOk());
- }
-}
-
-Y_UNIT_TEST_SUITE(ExternalFunction) {
- Y_UNIT_TEST(ValidUseFunctions) {
-
- UNIT_ASSERT(SqlToYql(
- "PROCESS plato.Input"
- " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo', <|a: 123, b: a + 641|>)"
- " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,"
- " CONCURRENCY=3, OPTIMIZE_FOR='CALLS'").IsOk());
-
- // use CALLS without quotes, as keyword
- UNIT_ASSERT(SqlToYql(
- "PROCESS plato.Input"
- " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo')"
- " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,"
- " OPTIMIZE_FOR=CALLS").IsOk());
-
- UNIT_ASSERT(SqlToYql(
- "PROCESS plato.Input"
- " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo', TableRow())"
- " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,"
- " CONCURRENCY=3").IsOk());
-
- UNIT_ASSERT(SqlToYql(
- "PROCESS plato.Input"
- " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo')"
- " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,"
- " CONCURRENCY=3, BATCH_SIZE=1000000, CONNECTION='yc-folder34fse-con',"
- " INIT=[0, 900]").IsOk());
-
- UNIT_ASSERT(SqlToYql(
- "PROCESS plato.Input"
- " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'bar', TableRow())"
- " WITH UNKNOWN_PARAM_1='837747712', UNKNOWN_PARAM_2=Tuple<Uint16, Utf8>,"
- " INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>").IsOk());
- }
-
-
- Y_UNIT_TEST(InValidUseFunctions) {
- ExpectFailWithError("PROCESS plato.Input USING some::udf(*) WITH INPUT_TYPE=Struct<a:Int32>",
- "<main>:1:33: Error: PROCESS without USING EXTERNAL FUNCTION doesn't allow WITH block\n");
-
- ExpectFailWithError("PROCESS plato.Input USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'jhhjfh88134d')"
- " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>"
- " ASSUME ORDER BY key",
- "<main>:1:129: Error: PROCESS with USING EXTERNAL FUNCTION doesn't allow ASSUME block\n");
-
- ExpectFailWithError("PROCESS plato.Input USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo', 'bar', 'baz')",
- "<main>:1:15: Error: EXTERNAL FUNCTION requires from 2 to 3 arguments, but got: 4\n");
-
- ExpectFailWithError("PROCESS plato.Input\n"
- " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo', <|field_1: a1, field_b: b1|>)\n"
- " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,\n"
- " CONCURRENCY=3, BATCH_SIZE=1000000, CONNECTION='yc-folder34fse-con',\n"
- " CONCURRENCY=5, INPUT_TYPE=Struct<b:Bool>,\n"
- " INIT=[0, 900]\n",
- "<main>:5:2: Error: WITH \"CONCURRENCY\" clause should be specified only once\n"
- "<main>:5:17: Error: WITH \"INPUT_TYPE\" clause should be specified only once\n");
- }
}
-Y_UNIT_TEST_SUITE(SqlToYQLErrors) {
- Y_UNIT_TEST(UdfSyntaxSugarMissingCall) {
- auto req = "SELECT Udf(DateTime::FromString, \"foo\" as RunConfig);";
- auto res = SqlToYql(req);
- TString a1 = Err2Str(res);
- TString a2("<main>:1:8: Error: Abstract Udf Node can't be used as a part of expression.\n");
- UNIT_ASSERT_NO_DIFF(a1, a2);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarIsNotCallable) {
- auto req = "SELECT Udf(123, \"foo\" as RunConfig);";
- auto res = SqlToYql(req);
- TString a1 = Err2Str(res);
- TString a2("<main>:1:8: Error: Udf: first argument must be a callable, like Foo::Bar\n");
- UNIT_ASSERT_NO_DIFF(a1, a2);
- }
-
- Y_UNIT_TEST(UdfSyntaxSugarNoArgs) {
- auto req = "SELECT Udf()();";
- auto res = SqlToYql(req);
- TString a1 = Err2Str(res);
- TString a2("<main>:1:8: Error: Udf: expected at least one argument\n");
- UNIT_ASSERT_NO_DIFF(a1, a2);
- }
-
- Y_UNIT_TEST(StrayUTF8) {
- /// 'c' in plato is russian here
- NYql::TAstParseResult res = SqlToYql("select * from сedar.Input");
- UNIT_ASSERT(!res.Root);
-
- TString a1 = Err2Str(res);
- TString a2(R"foo(<main>:1:14: Error: token recognition error at: 'с'
-)foo");
-
- UNIT_ASSERT_NO_DIFF(a1, a2);
- }
-
- Y_UNIT_TEST(IvalidStringLiteralWithEscapedBackslash) {
- NYql::TAstParseResult res1 = SqlToYql(R"foo($bar = 'a\\'b';)foo");
- NYql::TAstParseResult res2 = SqlToYql(R"foo($bar = "a\\"b";)foo");
- UNIT_ASSERT(!res1.Root);
- UNIT_ASSERT(!res2.Root);
-
- UNIT_ASSERT_NO_DIFF(Err2Str(res1), "<main>:1:13: Error: token recognition error at: '';'\n");
- UNIT_ASSERT_NO_DIFF(Err2Str(res2), "<main>:1:13: Error: token recognition error at: '\";'\n");
- }
-
- Y_UNIT_TEST(InvalidHexInStringLiteral) {
- NYql::TAstParseResult res = SqlToYql("select \"foo\\x1\\xfe\"");
- UNIT_ASSERT(!res.Root);
- TString a1 = Err2Str(res);
- TString a2 = "<main>:1:15: Error: Failed to parse string literal: Invalid hexadecimal value\n";
-
- UNIT_ASSERT_NO_DIFF(a1, a2);
- }
-
- Y_UNIT_TEST(InvalidOctalInMultilineStringLiteral) {
- NYql::TAstParseResult res = SqlToYql("select \"foo\n"
- "bar\n"
- "\\01\"");
- UNIT_ASSERT(!res.Root);
- TString a1 = Err2Str(res);
- TString a2 = "<main>:3:4: Error: Failed to parse string literal: Invalid octal value\n";
-
- UNIT_ASSERT_NO_DIFF(a1, a2);
- }
-
- Y_UNIT_TEST(InvalidDoubleAtString) {
- NYql::TAstParseResult res = SqlToYql("select @@@@@@");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: extraneous input '@' expecting {<EOF>, ';'}\n");
- }
-
- Y_UNIT_TEST(InvalidDoubleAtStringWhichWasAcceptedEarlier) {
- NYql::TAstParseResult res = SqlToYql("SELECT @@foo@@ @ @@bar@@");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:15: Error: mismatched input '@' expecting {<EOF>, ';'}\n");
- }
-
- Y_UNIT_TEST(InvalidStringFromTable) {
- NYql::TAstParseResult res = SqlToYql("select \"FOO\"\"BAR from plato.foo");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: token recognition error at: '\"BAR from plato.foo'\n");
- }
-
- Y_UNIT_TEST(InvalidDoubleAtStringFromTable) {
- NYql::TAstParseResult res = SqlToYql("select @@@@@@ from plato.foo");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: mismatched input '@' expecting {<EOF>, ';'}\n");
- }
-
- Y_UNIT_TEST(SelectInvalidSyntax) {
- NYql::TAstParseResult res = SqlToYql("select 1 form Wat");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:14: Error: extraneous input 'Wat' expecting {<EOF>, ';'}\n");
- }
-
- Y_UNIT_TEST(SelectNoCluster) {
- NYql::TAstParseResult res = SqlToYql("select foo from bar");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: No cluster name given and no default cluster is selected\n");
- }
-
- Y_UNIT_TEST(SelectDuplicateColumns) {
- NYql::TAstParseResult res = SqlToYql("select a, a from plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:11: Error: Unable to use duplicate column names. Collision in name: a\n");
- }
-
- Y_UNIT_TEST(SelectDuplicateLabels) {
- NYql::TAstParseResult res = SqlToYql("select a as foo, b as foo from plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:18: Error: Unable to use duplicate column names. Collision in name: foo\n");
- }
-
- Y_UNIT_TEST(SelectCaseWithoutThen) {
- NYql::TAstParseResult res = SqlToYql("select case when true 1;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:1:22: Error: missing THEN at \'1\'\n"
- "<main>:1:23: Error: extraneous input \';\' expecting {ELSE, END, WHEN}\n"
- );
- }
-
- Y_UNIT_TEST(SelectComplexCaseWithoutThen) {
- NYql::TAstParseResult res = SqlToYql(
- "SELECT *\n"
- "FROM plato.Input AS a\n"
- "WHERE CASE WHEN a.key = \"foo\" a.subkey ELSE a.value END\n"
- );
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:30: Error: missing THEN at 'a'\n");
- }
-
- Y_UNIT_TEST(SelectCaseWithoutEnd) {
- NYql::TAstParseResult res = SqlToYql("select case a when b then c end from plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: ELSE is required\n");
- }
-
- Y_UNIT_TEST(SelectWithBadAggregationNoInput) {
- NYql::TAstParseResult res = SqlToYql("select a, Min(b), c");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:8: Error: Column reference 'a'\n"
- "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:15: Error: Column reference 'b'\n"
- "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:19: Error: Column reference 'c'\n"
- );
- }
-
- Y_UNIT_TEST(SelectWithBadAggregation) {
- ExpectFailWithError("select count(*), 1 + key from plato.Input",
- "<main>:1:22: Error: Column `key` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(SelectWithBadAggregatedTerms) {
- ExpectFailWithError("select key, 2 * subkey from plato.Input group by key",
- "<main>:1:17: Error: Column `subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(SelectDistinctWithBadAggregation) {
- ExpectFailWithError("select distinct count(*), 1 + key from plato.Input",
- "<main>:1:31: Error: Column `key` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- ExpectFailWithError("select distinct key, 2 * subkey from plato.Input group by key",
- "<main>:1:26: Error: Column `subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(SelectWithBadAggregationInHaving) {
- ExpectFailWithError("select key from plato.Input group by key\n"
- "having \"f\" || value == \"foo\"",
- "<main>:2:15: Error: Column `value` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(JoinWithNonAggregatedColumnInProjection) {
- ExpectFailWithError("select a.key, 1 + b.subkey\n"
- "from plato.Input1 as a join plato.Input2 as b using(key)\n"
- "group by a.key;",
- "<main>:1:19: Error: Column `b.subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
-
- ExpectFailWithError("select a.key, 1 + b.subkey.x\n"
- "from plato.Input1 as a join plato.Input2 as b using(key)\n"
- "group by a.key;",
- "<main>:1:19: Error: Column must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(SelectWithBadAggregatedTermsWithSources) {
- ExpectFailWithError("select key, 1 + a.subkey\n"
- "from plato.Input1 as a\n"
- "group by a.key;",
- "<main>:1:17: Error: Column `a.subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- ExpectFailWithError("select key, 1 + a.subkey.x\n"
- "from plato.Input1 as a\n"
- "group by a.key;",
- "<main>:1:17: Error: Column must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(WarnForAggregationBySelectAlias) {
- NYql::TAstParseResult res = SqlToYql("select c + 1 as c from plato.Input\n"
- "group by c");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:2:11: Warning: GROUP BY will aggregate by column `c` instead of aggregating by SELECT expression with same alias, code: 4532\n"
- "<main>:1:10: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n");
-
- res = SqlToYql("select c + 1 as c from plato.Input\n"
- "group by Math::Floor(c + 2) as c;");
-
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:2:22: Warning: GROUP BY will aggregate by column `c` instead of aggregating by SELECT expression with same alias, code: 4532\n"
- "<main>:1:10: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n");
- }
-
- Y_UNIT_TEST(NoWarnForAggregationBySelectAliasWhenAggrFunctionsAreUsedInAlias) {
- NYql::TAstParseResult res = SqlToYql("select\n"
- " cast(avg(val) as int) as value,\n"
- " value as key\n"
- "from\n"
- " plato.Input\n"
- "group by value");
-
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- res = SqlToYql("select\n"
- " cast(avg(val) over w as int) as value,\n"
- " value as key\n"
- "from\n"
- " plato.Input\n"
- "group by value\n"
- "window w as ()");
-
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(NoWarnForAggregationBySelectAliasWhenQualifiedNameIsUsed) {
- NYql::TAstParseResult res = SqlToYql("select\n"
- " Unwrap(a.key) as key\n"
- "from plato.Input as a\n"
- "join plato.Input2 as b using(k)\n"
- "group by a.key;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- res = SqlToYql("select Unwrap(a.key) as key\n"
- "from plato.Input as a\n"
- "group by a.key;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(NoWarnForAggregationBySelectAliasWhenTrivialRenamingIsUsed) {
- NYql::TAstParseResult res = SqlToYql("select a.key as key\n"
- "from plato.Input as a\n"
- "group by key;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- res = SqlToYql("select key as key\n"
- "from plato.Input\n"
- "group by key;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(ErrorByAggregatingByExpressionWithSameExpressionInSelect) {
- ExpectFailWithError("select k * 2 from plato.Input group by k * 2",
- "<main>:1:8: Error: Column `k` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(ErrorForAggregationBySelectAlias) {
- ExpectFailWithError("select key, Math::Floor(1.1 + a.subkey) as foo\n"
- "from plato.Input as a\n"
- "group by a.key, foo;",
- "<main>:3:17: Warning: GROUP BY will aggregate by column `foo` instead of aggregating by SELECT expression with same alias, code: 4532\n"
- "<main>:1:19: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n"
- "<main>:1:31: Error: Column `a.subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
-
- ExpectFailWithError("select c + 1 as c from plato.Input\n"
- "group by Math::Floor(c + 2);",
- "<main>:2:22: Warning: GROUP BY will aggregate by column `c` instead of aggregating by SELECT expression with same alias, code: 4532\n"
- "<main>:1:10: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n"
- "<main>:1:8: Error: Column `c` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(ExplainQueryPlan) {
- NYql::TAstParseResult res = SqlToYql("EXPLAIN Q U E R Y PLAN SELECT 1;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "<main>:1:8: Error: mismatched input 'Q' expecting {");
- }
-
- Y_UNIT_TEST(SelectWithDuplicateGroupingColumns) {
- NYql::TAstParseResult res = SqlToYql("select c from plato.Input group by c, c");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Duplicate grouping column: c\n");
- }
-
- Y_UNIT_TEST(SelectWithBadAggregationInGrouping) {
- NYql::TAstParseResult res = SqlToYql("select a, Min(b), c group by c");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:30: Error: Column reference 'c'\n");
- }
-
- Y_UNIT_TEST(SelectWithOpOnBadAggregation) {
- ExpectFailWithError("select 1 + a + Min(b) from plato.Input",
- "<main>:1:12: Error: Column `a` must either be a key column in GROUP BY or it should be used in aggregation function\n");
- }
-
- Y_UNIT_TEST(SelectOrderByConstantNum) {
- NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by 1");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:36: Error: Unable to ORDER BY constant expression\n");
- }
-
- Y_UNIT_TEST(SelectOrderByConstantExpr) {
- NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by 1 * 42");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:38: Error: Unable to ORDER BY constant expression\n");
- }
-
- Y_UNIT_TEST(SelectOrderByConstantString) {
- NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by \"nest\"");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:36: Error: Unable to ORDER BY constant expression\n");
- }
-
- Y_UNIT_TEST(SelectOrderByAggregated) {
- NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by min(a)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:36: Error: Unable to ORDER BY aggregated values\n");
- }
-
- Y_UNIT_TEST(ErrorInOrderByExpresison) {
- NYql::TAstParseResult res = SqlToYql("select key, value from plato.Input order by (key as zey)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:45: Error: You should use in ORDER BY column name, qualified field, callable function or expression\n");
- }
-
- Y_UNIT_TEST(ErrorsInOrderByWhenColumnIsMissingInProjection) {
- ExpectFailWithError("select subkey from (select 1 as subkey) order by key", "<main>:1:50: Error: Column key is not in source column set\n");
- ExpectFailWithError("select subkey from plato.Input as a order by x.key", "<main>:1:46: Error: Unknown correlation name: x\n");
- ExpectFailWithError("select distinct a, b from plato.Input order by c", "<main>:1:48: Error: Column c is not in source column set. Did you mean a?\n");
- ExpectFailWithError("select count(*) as a from plato.Input order by c", "<main>:1:48: Error: Column c is not in source column set. Did you mean a?\n");
- ExpectFailWithError("select count(*) as a, b, from plato.Input group by b order by c", "<main>:1:63: Error: Column c is not in source column set. Did you mean a?\n");
- UNIT_ASSERT(SqlToYql("select a, b from plato.Input order by c").IsOk());
- }
-
- Y_UNIT_TEST(SelectAggregatedWhere) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input where count(key)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:33: Error: Can not use aggregated values in filtering\n");
- }
-
- Y_UNIT_TEST(DoubleFrom) {
- NYql::TAstParseResult res = SqlToYql("from plato.Input select * from plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:27: Error: Only one FROM clause is allowed\n");
- }
-
- Y_UNIT_TEST(SelectJoinMissingCorrName) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input1 as a join plato.Input2 as b on a.key == key");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:65: Error: JOIN: column requires correlation name\n");
- }
-
- Y_UNIT_TEST(SelectJoinMissingCorrName1) {
- NYql::TAstParseResult res = SqlToYql(
- "use plato;\n"
- "$foo = select * from Input1;\n"
- "select * from Input2 join $foo USING(key);\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:27: Error: JOIN: missing correlation name for source\n");
- }
-
- Y_UNIT_TEST(SelectJoinMissingCorrName2) {
- NYql::TAstParseResult res = SqlToYql(
- "use plato;\n"
- "$foo = select * from Input1;\n"
- "select * from Input2 cross join $foo;\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:33: Error: JOIN: missing correlation name for source\n");
- }
-
- Y_UNIT_TEST(SelectJoinEmptyCorrNames) {
- NYql::TAstParseResult res = SqlToYql(
- "$left = (SELECT * FROM plato.Input1 LIMIT 2);\n"
- "$right = (SELECT * FROM plato.Input2 LIMIT 2);\n"
- "SELECT * FROM $left FULL JOIN $right USING (key);\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:45: Error: At least one correlation name is required in join\n");
- }
-
- Y_UNIT_TEST(SelectJoinSameCorrNames) {
- NYql::TAstParseResult res = SqlToYql("SELECT Input.key FROM plato.Input JOIN plato.Input1 ON Input.key == Input.subkey\n");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:66: Error: JOIN: different correlation names are required for joined tables\n");
- }
-
- Y_UNIT_TEST(SelectJoinConstPredicateArg) {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input1 as A JOIN plato.Input2 as B ON A.key == B.key AND A.subkey == \"wtf\"\n");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:87: Error: JOIN: each equality predicate argument must depend on exactly one JOIN input\n");
- }
-
- Y_UNIT_TEST(SelectJoinNonEqualityPredicate) {
- NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input1 as A JOIN plato.Input2 as B ON A.key == B.key AND A.subkey > B.subkey\n");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:87: Error: JOIN ON expression must be a conjunction of equality predicates\n");
- }
-
- Y_UNIT_TEST(SelectEquiJoinCorrNameOutOfScope) {
- NYql::TAstParseResult res = SqlToYql(
- "PRAGMA equijoin;\n"
- "SELECT * FROM plato.A JOIN plato.B ON A.key == C.key JOIN plato.C ON A.subkey == C.subkey;\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:45: Error: JOIN: can not use source: C in equality predicate, it is out of current join scope\n");
- }
-
- Y_UNIT_TEST(SelectEquiJoinNoRightSource) {
- NYql::TAstParseResult res = SqlToYql(
- "PRAGMA equijoin;\n"
- "SELECT * FROM plato.A JOIN plato.B ON A.key == B.key JOIN plato.C ON A.subkey == B.subkey;\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:79: Error: JOIN ON equality predicate must have one of its arguments from the rightmost source\n");
- }
-
- Y_UNIT_TEST(SelectEquiJoinOuterWithoutType) {
- NYql::TAstParseResult res = SqlToYql(
- "SELECT * FROM plato.A Outer JOIN plato.B ON A.key == B.key;\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:23: Error: Invalid join type: OUTER JOIN. OUTER keyword is optional and can only be used after LEFT, RIGHT or FULL\n");
- }
-
- Y_UNIT_TEST(SelectEquiJoinOuterWithWrongType) {
- NYql::TAstParseResult res = SqlToYql(
- "SELECT * FROM plato.A LEFT semi OUTER JOIN plato.B ON A.key == B.key;\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:33: Error: Invalid join type: LEFT SEMI OUTER JOIN. OUTER keyword is optional and can only be used after LEFT, RIGHT or FULL\n");
- }
-
- Y_UNIT_TEST(InsertNoCluster) {
- NYql::TAstParseResult res = SqlToYql("insert into Output (foo) values (1)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: No cluster name given and no default cluster is selected\n");
- }
-
- Y_UNIT_TEST(InsertValuesNoLabels) {
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output values (1)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: INSERT INTO ... VALUES requires specification of table columns\n");
- }
-
- Y_UNIT_TEST(UpsertValuesNoLabelsKikimr) {
- NYql::TAstParseResult res = SqlToYql("upsert into plato.Output values (1)", 10, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: UPSERT INTO ... VALUES requires specification of table columns\n");
- }
-
- Y_UNIT_TEST(ReplaceValuesNoLabelsKikimr) {
- NYql::TAstParseResult res = SqlToYql("replace into plato.Output values (1)", 10, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:20: Error: REPLACE INTO ... VALUES requires specification of table columns\n");
- }
-
- Y_UNIT_TEST(InsertValuesInvalidLabels) {
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output (foo) values (1, 2)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:27: Error: VALUES have 2 columns, INSERT INTO expects: 1\n");
- }
-
- Y_UNIT_TEST(BuiltinFileOpNoArgs) {
- NYql::TAstParseResult res = SqlToYql("select FilePath()");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: FilePath() requires exactly 1 arguments, given: 0\n");
- }
-
- Y_UNIT_TEST(ProcessWithHaving) {
- NYql::TAstParseResult res = SqlToYql("process plato.Input using some::udf(value) having value == 1");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:15: Error: PROCESS does not allow HAVING yet! You may request it on yql@ maillist.\n");
- }
-
- Y_UNIT_TEST(ReduceNoBy) {
- NYql::TAstParseResult res = SqlToYql("reduce plato.Input using some::udf(value)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: mismatched input 'using' expecting {',', ON, PRESORT}\n");
- }
-
- Y_UNIT_TEST(ReduceDistinct) {
- NYql::TAstParseResult res = SqlToYql("reduce plato.Input on key using some::udf(distinct value)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:43: Error: DISTINCT can not be used in PROCESS/REDUCE\n");
- }
-
- Y_UNIT_TEST(CreateTableWithView) {
- NYql::TAstParseResult res = SqlToYql("CREATE TABLE plato.foo:bar (key INT);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:22: Error: mismatched input ':' expecting '('\n");
- }
-
- Y_UNIT_TEST(AsteriskWithSomethingAfter) {
- NYql::TAstParseResult res = SqlToYql("select *, LENGTH(value) from plato.Input;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Unable to use plain '*' with other projection items. Please use qualified asterisk instead: '<table>.*' (<table> can be either table name or table alias).\n");
- }
-
- Y_UNIT_TEST(AsteriskWithSomethingBefore) {
- NYql::TAstParseResult res = SqlToYql("select LENGTH(value), * from plato.Input;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:23: Error: Unable to use plain '*' with other projection items. Please use qualified asterisk instead: '<table>.*' (<table> can be either table name or table alias).\n");
- }
-
- Y_UNIT_TEST(DuplicatedQualifiedAsterisk) {
- NYql::TAstParseResult res = SqlToYql("select in.*, key, in.* from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: Unable to use twice same quialified asterisk. Invalid source: in\n");
- }
-
- Y_UNIT_TEST(BrokenLabel) {
- NYql::TAstParseResult res = SqlToYql("select in.*, key as `funny.label` from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:14: Error: Unable to use '.' in column name. Invalid column name: funny.label\n");
- }
-
- Y_UNIT_TEST(KeyConflictDetect0) {
- NYql::TAstParseResult res = SqlToYql("select key, in.key as key from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:13: Error: Unable to use duplicate column names. Collision in name: key\n");
- }
-
- Y_UNIT_TEST(KeyConflictDetect1) {
- NYql::TAstParseResult res = SqlToYql("select length(key) as key, key from plato.Input;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:28: Error: Unable to use duplicate column names. Collision in name: key\n");
- }
-
- Y_UNIT_TEST(KeyConflictDetect2) {
- NYql::TAstParseResult res = SqlToYql("select key, in.key from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
- }
-
- Y_UNIT_TEST(AutogenerationAliasWithCollisionConflict1) {
- UNIT_ASSERT(SqlToYql("select LENGTH(Value), key as column0 from plato.Input;").IsOk());
- }
-
- Y_UNIT_TEST(AutogenerationAliasWithCollisionConflict2) {
- UNIT_ASSERT(SqlToYql("select key as column1, LENGTH(Value) from plato.Input;").IsOk());
- }
-
- Y_UNIT_TEST(MissedSourceTableForQualifiedAsteriskOnSimpleSelect) {
- NYql::TAstParseResult res = SqlToYql("use plato; select Intop.*, Input.key from plato.Input;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: Unknown correlation name: Intop\n");
- }
-
- Y_UNIT_TEST(MissedSourceTableForQualifiedAsteriskOnJoin) {
- NYql::TAstParseResult res = SqlToYql("use plato; select tmissed.*, t2.*, t1.key from plato.Input as t1 join plato.Input as t2 on t1.key==t2.key;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: Unknown correlation name for asterisk: tmissed\n");
- }
-
- Y_UNIT_TEST(UnableToReferenceOnNotExistSubcolumn) {
- NYql::TAstParseResult res = SqlToYql("select b.subkey from (select key from plato.Input as a) as b;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Column subkey is not in source column set\n");
- }
-
- Y_UNIT_TEST(ConflictOnSameNameWithQualify0) {
- NYql::TAstParseResult res = SqlToYql("select in.key, in.key as key from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
- }
-
- Y_UNIT_TEST(ConflictOnSameNameWithQualify1) {
- NYql::TAstParseResult res = SqlToYql("select in.key, length(key) as key from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
- }
-
- Y_UNIT_TEST(ConflictOnSameNameWithQualify2) {
- NYql::TAstParseResult res = SqlToYql("select key, in.key from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
- }
-
- Y_UNIT_TEST(ConflictOnSameNameWithQualify3) {
- NYql::TAstParseResult res = SqlToYql("select in.key, subkey as key from plato.Input as in;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
- }
-
- Y_UNIT_TEST(SelectFlattenBySameColumns) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key, key as kk)");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:46: Error: Duplicate column name found: key in FlattenBy section\n");
- }
-
- Y_UNIT_TEST(SelectFlattenBySameAliases) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key as kk, subkey as kk);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Error: Duplicate alias found: kk in FlattenBy section\n");
- }
-
- Y_UNIT_TEST(SelectFlattenByExprSameAliases) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key as kk, ListSkip(subkey,1) as kk);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Error: Collision between alias and column name: kk in FlattenBy section\n");
- }
-
- Y_UNIT_TEST(SelectFlattenByConflictNameAndAlias0) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key, subkey as key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:46: Error: Collision between alias and column name: key in FlattenBy section\n");
- }
-
- Y_UNIT_TEST(SelectFlattenByConflictNameAndAlias1) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key as kk, subkey as key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Error: Collision between alias and column name: key in FlattenBy section\n");
- }
-
- Y_UNIT_TEST(SelectFlattenByExprConflictNameAndAlias1) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key as kk, ListSkip(subkey,1) as key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Error: Duplicate column name found: key in FlattenBy section\n");
- }
-
- Y_UNIT_TEST(SelectFlattenByUnnamedExpr) {
- NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key, ListSkip(key, 1))");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:46: Error: Unnamed expression after FLATTEN BY is not allowed\n");
- }
-
- Y_UNIT_TEST(UseInOnStrings) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input where \"foo\" in \"foovalue\";");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:42: Error: Unable to use IN predicate with string argument, it won't search substring - "
- "expecting tuple, list, dict or single column table source\n");
- }
-
- Y_UNIT_TEST(UseSubqueryInScalarContextInsideIn) {
- NYql::TAstParseResult res = SqlToYql("$q = (select key from plato.Input); select * from plato.Input where subkey in ($q);");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:79: Warning: Using subrequest in scalar context after IN, "
- "perhaps you should remove parenthesis here, code: 4501\n");
- }
-
- Y_UNIT_TEST(InHintsWithKeywordClash) {
- NYql::TAstParseResult res = SqlToYql("SELECT COMPACT FROM plato.Input WHERE COMPACT IN COMPACT `COMPACT`(1,2,3)");
- UNIT_ASSERT(!res.Root);
- // should try to parse last compact as call expression
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:58: Error: Unknown builtin: COMPACT\n");
- }
-
- Y_UNIT_TEST(ErrorColumnPosition) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato;\n"
- "SELECT \n"
- "value FROM (\n"
- "select key from Input\n"
- ");\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:1: Error: Column value is not in source column set\n");
- }
-
- Y_UNIT_TEST(PrimaryViewAbortMapReduce) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input VIEW PRIMARY KEY");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: primary view is not supported for yt tables\n");
- }
-
- Y_UNIT_TEST(InsertAbortMapReduce) {
- NYql::TAstParseResult res = SqlToYql("INSERT OR ABORT INTO plato.Output SELECT key FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: INSERT OR ABORT INTO is not supported for yt tables\n");
- }
-
- Y_UNIT_TEST(ReplaceIntoMapReduce) {
- NYql::TAstParseResult res = SqlToYql("REPLACE INTO plato.Output SELECT key FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: Meaning of REPLACE INTO has been changed, now you should use INSERT INTO <table> WITH TRUNCATE ... for yt\n");
- }
-
- Y_UNIT_TEST(UpsertIntoMapReduce) {
- NYql::TAstParseResult res = SqlToYql("UPSERT INTO plato.Output SELECT key FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: UPSERT INTO is not supported for yt tables\n");
- }
-
- Y_UNIT_TEST(UpdateMapReduce) {
- NYql::TAstParseResult res = SqlToYql("UPDATE plato.Output SET value = value + 1 WHERE key < 1");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: UPDATE is unsupported for yt\n");
- }
-
- Y_UNIT_TEST(DeleteMapReduce) {
- NYql::TAstParseResult res = SqlToYql("DELETE FROM plato.Output WHERE key < 1");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: DELETE is unsupported for yt\n");
- }
-
- Y_UNIT_TEST(ReplaceIntoWithTruncate) {
- NYql::TAstParseResult res = SqlToYql("REPLACE INTO plato.Output WITH TRUNCATE SELECT key FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:32: Error: Unable REPLACE INTO with truncate mode\n");
- }
-
- Y_UNIT_TEST(UpsertIntoWithTruncate) {
- NYql::TAstParseResult res = SqlToYql("UPSERT INTO plato.Output WITH TRUNCATE SELECT key FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:31: Error: Unable UPSERT INTO with truncate mode\n");
- }
-
- Y_UNIT_TEST(InsertIntoWithTruncateKikimr) {
- NYql::TAstParseResult res = SqlToYql("INSERT INTO plato.Output WITH TRUNCATE SELECT key FROM plato.Input", 10, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: INSERT INTO WITH TRUNCATE is not supported for kikimr tables\n");
- }
-
- Y_UNIT_TEST(InsertIntoWithWrongArgumentCount) {
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output with truncate (key, value, subkey) values (5, '1', '2', '3');");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:53: Error: VALUES have 4 columns, INSERT INTO ... WITH TRUNCATE expects: 3\n");
- }
-
- Y_UNIT_TEST(UpsertWithWrongArgumentCount) {
- NYql::TAstParseResult res = SqlToYql("upsert into plato.Output (key, value, subkey) values (2, '3');", 10, TString(NYql::KikimrProviderName));
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:39: Error: VALUES have 2 columns, UPSERT INTO expects: 3\n");
- }
-
- Y_UNIT_TEST(GroupingSetByExprWithoutAlias) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY GROUPING SETS (cast(key as uint32), subkey);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:53: Error: Unnamed expressions are not supported in GROUPING SETS. Please use '<expr> AS <name>'.\n");
- }
-
- Y_UNIT_TEST(GroupingSetByExprWithoutAlias2) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY subkey || subkey, GROUPING SETS (\n"
- "cast(key as uint32), subkey);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:1: Error: Unnamed expressions are not supported in GROUPING SETS. Please use '<expr> AS <name>'.\n");
- }
-
- Y_UNIT_TEST(CubeByExprWithoutAlias) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY CUBE (key, subkey / key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:56: Error: Unnamed expressions are not supported in CUBE. Please use '<expr> AS <name>'.\n");
- }
-
- Y_UNIT_TEST(RollupByExprWithoutAlias) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY ROLLUP (subkey / key);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:53: Error: Unnamed expressions are not supported in ROLLUP. Please use '<expr> AS <name>'.\n");
- }
-
- Y_UNIT_TEST(GroupByHugeCubeDeniedNoPragma) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY CUBE (key, subkey, value, key + subkey as sum, key - subkey as sub, key + val as keyval);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:119: Error: GROUP BY CUBE is allowed only for 5 columns, but you use 6\n");
- }
-
- Y_UNIT_TEST(GroupByInvalidPragma) {
- NYql::TAstParseResult res = SqlToYql("PRAGMA GroupByCubeLimit = '-4';");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:27: Error: Expected unsigned integer literal as a single argument for: GroupByCubeLimit\n");
- }
-
- Y_UNIT_TEST(GroupByHugeCubeDeniedPragme) {
- NYql::TAstParseResult res = SqlToYql("PRAGMA GroupByCubeLimit = '4'; SELECT key FROM plato.Input GROUP BY CUBE (key, subkey, value, key + subkey as sum, key - subkey as sub);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:132: Error: GROUP BY CUBE is allowed only for 4 columns, but you use 5\n");
- }
-
- Y_UNIT_TEST(GroupByFewBigCubes) {
- NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY CUBE(key, subkey, key + subkey as sum), CUBE(value, value + key + subkey as total);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Unable to GROUP BY more than 64 groups, you try use 80 groups\n");
- }
-
- Y_UNIT_TEST(GroupByFewBigCubesWithPragmaLimit) {
- NYql::TAstParseResult res = SqlToYql("PRAGMA GroupByLimit = '16'; SELECT key FROM plato.Input GROUP BY GROUPING SETS(key, subkey, key + subkey as sum), ROLLUP(value, value + key + subkey as total);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:29: Error: Unable to GROUP BY more than 16 groups, you try use 18 groups\n");
- }
-
- Y_UNIT_TEST(NoGroupingColumn0) {
- NYql::TAstParseResult res = SqlToYql(
- "select count(1), key_first, val_first, grouping(key_first, val_first, nomind) as group\n"
- "from plato.Input group by grouping sets (cast(key as uint32) /100 as key_first, Substring(value, 1, 1) as val_first);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:71: Error: Column 'nomind' is not a grouping column\n");
- }
-
- Y_UNIT_TEST(NoGroupingColumn1) {
- NYql::TAstParseResult res = SqlToYql("select count(1), grouping(key, value) as group_duo from plato.Input group by cube (key, subkey);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:32: Error: Column 'value' is not a grouping column\n");
- }
-
- Y_UNIT_TEST(EmptyAccess0) {
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output (list0, list1) values (AsList(0, 1, 2), AsList(``));");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:73: Error: Column reference \"\" is not allowed in current scope\n");
- }
-
- Y_UNIT_TEST(EmptyAccess1) {
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output (list0, list1) values (AsList(0, 1, 2), ``);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:66: Error: Column reference \"\" is not allowed in current scope\n");
- }
-
- Y_UNIT_TEST(UseUnknownColumnInInsert) {
- NYql::TAstParseResult res = SqlToYql("insert into plato.Output (list0, list1) values (AsList(0, 1, 2), AsList(`test`));");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:73: Error: Column reference \"test\" is not allowed in current scope\n");
- }
-
- Y_UNIT_TEST(GroupByEmptyColumn) {
- NYql::TAstParseResult res = SqlToYql("select count(1) from plato.Input group by ``;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:43: Error: Column name can not be empty\n");
- }
-
- Y_UNIT_TEST(ConvertNumberOutOfBase) {
- NYql::TAstParseResult res = SqlToYql("select 0o80l;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to parse number from string: 0o80l, char: '8' is out of base: 8\n");
- }
-
- Y_UNIT_TEST(ConvertNumberOutOfRangeForInt64ButFitsInUint64) {
- NYql::TAstParseResult res = SqlToYql("select 0xc000000000000000l;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to parse 13835058055282163712 as integer literal of Int64 type: value out of range for Int64\n");
- }
-
- Y_UNIT_TEST(ConvertNumberOutOfRangeUint64) {
- NYql::TAstParseResult res = SqlToYql("select 0xc0000000000000000l;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to parse number from string: 0xc0000000000000000l, number limit overflow\n");
-
- res = SqlToYql("select 1234234543563435151456;\n");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to parse number from string: 1234234543563435151456, number limit overflow\n");
- }
-
- Y_UNIT_TEST(ConvertNumberNegativeOutOfRange) {
- NYql::TAstParseResult res = SqlToYql("select -9223372036854775808;\n"
- "select -9223372036854775809;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:8: Error: Failed to parse negative integer: -9223372036854775809, number limit overflow\n");
- }
-
- Y_UNIT_TEST(InvaildUsageReal0) {
- NYql::TAstParseResult res = SqlToYql("select .0;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "<main>:1:7: Error: extraneous input '.' expecting {");
- }
-
- Y_UNIT_TEST(InvaildUsageReal1) {
- NYql::TAstParseResult res = SqlToYql("select .0f;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "<main>:1:7: Error: extraneous input '.' expecting {");
- }
-
- Y_UNIT_TEST(InvaildUsageWinFunctionWithoutWindow) {
- NYql::TAstParseResult res = SqlToYql("select lead(key, 2) from plato.Input;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to use window function Lead without window specification\n");
- }
-
- Y_UNIT_TEST(DropTableWithIfExists) {
- NYql::TAstParseResult res = SqlToYql("DROP TABLE IF EXISTS plato.foo;");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop_if_exists"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(TooManyErrors) {
- const char* q = R"(
- USE plato;
- select A, B, C, D, E, F, G, H, I, J, K, L, M, N from (select b from `abc`);
-)";
-
- NYql::TAstParseResult res = SqlToYql(q, 10);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- R"(<main>:3:16: Error: Column A is not in source column set. Did you mean b?
-<main>:3:19: Error: Column B is not in source column set. Did you mean b?
-<main>:3:22: Error: Column C is not in source column set. Did you mean b?
-<main>:3:25: Error: Column D is not in source column set. Did you mean b?
-<main>:3:28: Error: Column E is not in source column set. Did you mean b?
-<main>:3:31: Error: Column F is not in source column set. Did you mean b?
-<main>:3:34: Error: Column G is not in source column set. Did you mean b?
-<main>:3:37: Error: Column H is not in source column set. Did you mean b?
-<main>:3:40: Error: Column I is not in source column set. Did you mean b?
-<main>: Error: Too many issues, code: 1
-)");
- };
-
- Y_UNIT_TEST(ShouldCloneBindingForNamedParameter) {
- NYql::TAstParseResult res = SqlToYql(R"($f = () -> {
- $value_type = TypeOf(1);
- $pair_type = StructType(
- TypeOf("2") AS key,
- $value_type AS value
- );
-
- RETURN TupleType(
- ListType($value_type),
- $pair_type);
-};
-
-select FormatType($f());
-)");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(BlockedInvalidFrameBounds) {
- auto check = [](const TString& frame, const TString& err) {
- const TString prefix = "SELECT SUM(x) OVER w FROM plato.Input WINDOW w AS (PARTITION BY key ORDER BY subkey\n";
- NYql::TAstParseResult res = SqlToYql(prefix + frame + ")");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), err);
- };
-
- check("ROWS UNBOUNDED FOLLOWING", "<main>:2:5: Error: Frame cannot start from UNBOUNDED FOLLOWING\n");
- check("ROWS BETWEEN 5 PRECEDING AND UNBOUNDED PRECEDING", "<main>:2:29: Error: Frame cannot end with UNBOUNDED PRECEDING\n");
- check("ROWS BETWEEN CURRENT ROW AND 5 PRECEDING", "<main>:2:13: Error: Frame cannot start from CURRENT ROW and end with PRECEDING\n");
- check("ROWS BETWEEN 5 FOLLOWING AND CURRENT ROW", "<main>:2:14: Error: Frame cannot start from FOLLOWING and end with CURRENT ROW\n");
- check("ROWS BETWEEN 5 FOLLOWING AND 5 PRECEDING", "<main>:2:14: Error: Frame cannot start from FOLLOWING and end with PRECEDING\n");
- }
-
- Y_UNIT_TEST(BlockedRangeValueWithoutSingleOrderBy) {
- UNIT_ASSERT(SqlToYql("SELECT COUNT(*) OVER (RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM plato.Input").IsOk());
- UNIT_ASSERT(SqlToYql("SELECT COUNT(*) OVER (RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM plato.Input").IsOk());
-
- auto res = SqlToYql("SELECT COUNT(*) OVER (RANGE 5 PRECEDING) FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:29: Error: RANGE with <offset> PRECEDING/FOLLOWING requires exactly one expression in ORDER BY partition clause\n");
-
- res = SqlToYql("SELECT COUNT(*) OVER (ORDER BY key, value RANGE 5 PRECEDING) FROM plato.Input");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Error: RANGE with <offset> PRECEDING/FOLLOWING requires exactly one expression in ORDER BY partition clause\n");
- }
-
- Y_UNIT_TEST(NoColumnsInFrameBounds) {
- NYql::TAstParseResult res = SqlToYql(
- "SELECT SUM(x) OVER w FROM plato.Input WINDOW w AS (ROWS BETWEEN\n"
- " 1 + key PRECEDING AND 2 + key FOLLOWING);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:6: Error: Column reference \"key\" is not allowed in current scope\n");
- }
-
- Y_UNIT_TEST(WarnOnEmptyFrameBounds) {
- NYql::TAstParseResult res = SqlToYql(
- "SELECT SUM(x) OVER w FROM plato.Input WINDOW w AS (PARTITION BY key ORDER BY subkey\n"
- "ROWS BETWEEN 10 FOLLOWING AND 5 FOLLOWING)");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:14: Warning: Used frame specification implies empty window frame, code: 4520\n");
- }
-
- Y_UNIT_TEST(WarnOnRankWithUnorderedWindow) {
- NYql::TAstParseResult res = SqlToYql("SELECT RANK() OVER w FROM plato.Input WINDOW w AS ()");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Warning: Rank() is used with unordered window - all rows will be considered equal to each other, code: 4521\n");
- }
-
- Y_UNIT_TEST(WarnOnRankExprWithUnorderedWindow) {
- NYql::TAstParseResult res = SqlToYql("SELECT RANK(key) OVER w FROM plato.Input WINDOW w AS ()");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Warning: Rank(<expression>) is used with unordered window - the result is likely to be undefined, code: 4521\n");
- }
-
- Y_UNIT_TEST(AnyAsTableName) {
- NYql::TAstParseResult res = SqlToYql("use plato; select * from any;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:28: Error: no viable alternative at input 'any;'\n");
- }
-
- Y_UNIT_TEST(IncorrectOrderOfLambdaOptionalArgs) {
- NYql::TAstParseResult res = SqlToYql("$f = ($x?, $y)->($x + $y); select $f(1);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Non-optional argument can not follow optional one\n");
- }
-
- Y_UNIT_TEST(IncorrectOrderOfActionOptionalArgs) {
- NYql::TAstParseResult res = SqlToYql("define action $f($x?, $y) as select $x,$y; end define; do $f(1);");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:23: Error: Non-optional argument can not follow optional one\n");
- }
-
- Y_UNIT_TEST(NotAllowedQuestionOnNamedNode) {
- NYql::TAstParseResult res = SqlToYql("$f = 1; select $f?;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:18: Error: Unexpected token '?' at the end of expression\n");
- }
-
- Y_UNIT_TEST(AnyAndCrossJoin) {
- NYql::TAstParseResult res = SqlToYql("use plato; select * from any Input1 cross join Input2");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:26: Error: ANY should not be used with Cross JOIN\n");
-
- res = SqlToYql("use plato; select * from Input1 cross join any Input2");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:44: Error: ANY should not be used with Cross JOIN\n");
- }
-
- Y_UNIT_TEST(AnyWithCartesianProduct) {
- NYql::TAstParseResult res = SqlToYql("pragma AnsiImplicitCrossJoin; use plato; select * from any Input1, Input2");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:56: Error: ANY should not be used with Cross JOIN\n");
-
- res = SqlToYql("pragma AnsiImplicitCrossJoin; use plato; select * from Input1, any Input2");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:64: Error: ANY should not be used with Cross JOIN\n");
- }
-
- Y_UNIT_TEST(ErrorPlainEndAsInlineActionTerminator) {
- NYql::TAstParseResult res = SqlToYql(
- "do begin\n"
- " select 1\n"
- "; end\n");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:0: Error: missing DO at '<EOF>'\n");
- }
-
- Y_UNIT_TEST(ErrorMultiWayJoinWithUsing) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato;\n"
- "PRAGMA DisableSimpleColumns;\n"
- "SELECT *\n"
- "FROM Input1 AS a\n"
- "JOIN Input2 AS b USING(key)\n"
- "JOIN Input3 AS c ON a.key = c.key;\n"
- );
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:5:24: Error: Multi-way JOINs should be connected with ON clause instead of USING clause\n"
- );
- }
-
- Y_UNIT_TEST(RequireLabelInFlattenByWithDot) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input flatten by x.y");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:1:40: Error: Unnamed expression after FLATTEN BY is not allowed\n"
- );
- }
-
- Y_UNIT_TEST(WarnUnnamedColumns) {
- NYql::TAstParseResult res = SqlToYql(
- "PRAGMA WarnUnnamedColumns;\n"
- "\n"
- "SELECT key, subkey, key || subkey FROM plato.Input ORDER BY subkey;\n");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:28: Warning: Autogenerated column name column2 will be used for expression, code: 4516\n");
- }
-
- Y_UNIT_TEST(WarnSourceColumnMismatch) {
- NYql::TAstParseResult res = SqlToYql(
- "insert into plato.Output (key, subkey, new_value, one_more_value) select key as Key, subkey, value, \"x\" from plato.Input;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:51: Warning: Column names in SELECT don't match column specification in parenthesis. \"key\" doesn't match \"Key\". \"new_value\" doesn't match \"value\", code: 4517\n");
- }
-
- Y_UNIT_TEST(YtCaseInsensitive) {
- NYql::TAstParseResult res = SqlToYql("select * from PlatO.foo;");
- UNIT_ASSERT(res.Root);
-
- res = SqlToYql("use PlatO; select * from foo;");
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(KikimrCaseSensitive) {
- NYql::TAstParseResult res = SqlToYql("select * from PlatO.foo;", 10, "kikimr");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:15: Error: Unknown cluster: PlatO\n");
-
- res = SqlToYql("use PlatO; select * from foo;", 10, "kikimr");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:5: Error: Unknown cluster: PlatO\n");
- }
-
- Y_UNIT_TEST(DiscoveryModeForbidden) {
- NYql::TAstParseResult res = SqlToYqlWithMode("insert into plato.Output select * from plato.range(\"\", Input1, Input4)", NSQLTranslation::ESqlMode::DISCOVERY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: range is not allowed in Discovery mode, code: 4600\n");
-
- res = SqlToYqlWithMode("insert into plato.Output select * from plato.like(\"\", \"Input%\")", NSQLTranslation::ESqlMode::DISCOVERY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: like is not allowed in Discovery mode, code: 4600\n");
-
- res = SqlToYqlWithMode("insert into plato.Output select * from plato.regexp(\"\", \"Input.\")", NSQLTranslation::ESqlMode::DISCOVERY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: regexp is not allowed in Discovery mode, code: 4600\n");
-
- res = SqlToYqlWithMode("insert into plato.Output select * from plato.filter(\"\", ($name) -> { return find($name, \"Input\") is not null; })", NSQLTranslation::ESqlMode::DISCOVERY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: filter is not allowed in Discovery mode, code: 4600\n");
-
- res = SqlToYqlWithMode("select Path from plato.folder(\"\") where Type == \"table\"", NSQLTranslation::ESqlMode::DISCOVERY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:18: Error: folder is not allowed in Discovery mode, code: 4600\n");
- }
-
- Y_UNIT_TEST(YsonFuncWithoutArgs) {
- UNIT_ASSERT(SqlToYql("SELECT Yson::SerializeText(Yson::From());").IsOk());
- }
-
- Y_UNIT_TEST(CanNotUseOrderByInNonLastSelectInUnionAllChain) {
- auto req = "pragma AnsiOrderByLimitInUnionAll;\n"
- "use plato;\n"
- "\n"
- "select * from Input order by key\n"
- "union all\n"
- "select * from Input order by key limit 1;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:21: Error: ORDER BY within UNION ALL is only allowed after last subquery\n");
- }
-
- Y_UNIT_TEST(CanNotUseLimitInNonLastSelectInUnionAllChain) {
- auto req = "pragma AnsiOrderByLimitInUnionAll;\n"
- "use plato;\n"
- "\n"
- "select * from Input limit 1\n"
- "union all\n"
- "select * from Input order by key limit 1;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:21: Error: LIMIT within UNION ALL is only allowed after last subquery\n");
- }
-
- Y_UNIT_TEST(CanNotUseDiscardInNonFirstSelectInUnionAllChain) {
- auto req = "pragma AnsiOrderByLimitInUnionAll;\n"
- "use plato;\n"
- "\n"
- "select * from Input\n"
- "union all\n"
- "discard select * from Input;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:6:1: Error: DISCARD within UNION ALL is only allowed before first subquery\n");
- }
-
- Y_UNIT_TEST(CanNotUseIntoResultInNonLastSelectInUnionAllChain) {
- auto req = "use plato;\n"
- "pragma AnsiOrderByLimitInUnionAll;\n"
- "\n"
- "select * from Input\n"
- "union all\n"
- "discard select * from Input;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:6:1: Error: DISCARD within UNION ALL is only allowed before first subquery\n");
- }
-
- Y_UNIT_TEST(YsonStrictInvalidPragma) {
- auto res = SqlToYql("pragma yson.Strict = \"wrong\";");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:22: Error: Expected 'true', 'false' or no parameter for: Strict\n");
- }
-
- Y_UNIT_TEST(WarnTableNameInSomeContexts) {
- UNIT_ASSERT(SqlToYql("use plato; select TableName() from Input;").IsOk());
- UNIT_ASSERT(SqlToYql("use plato; select TableName(\"aaaa\");").IsOk());
- UNIT_ASSERT(SqlToYql("select TableName(\"aaaa\", \"yt\");").IsOk());
-
- auto res = SqlToYql("select TableName() from plato.Input;");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: TableName requires either service name as second argument or current cluster name\n");
-
- res = SqlToYql("use plato;\n"
- "select TableName() from Input1 as a join Input2 as b using(key);");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:8: Warning: TableName() may produce empty result when used in ambiguous context (with JOIN), code: 4525\n");
-
- res = SqlToYql("use plato;\n"
- "select SOME(TableName()), key from Input group by key;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:13: Warning: TableName() will produce empty result when used with aggregation.\n"
- "Please consult documentation for possible workaround, code: 4525\n");
- }
-
- Y_UNIT_TEST(WarnOnDistincWithHavingWithoutAggregations) {
- auto res = SqlToYql("select distinct key from plato.Input having key != '0';");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Warning: The usage of HAVING without aggregations with SELECT DISTINCT is non-standard and will stop working soon. Please use WHERE instead., code: 4526\n");
- }
-
- Y_UNIT_TEST(FlattenByExprWithNestedNull) {
- auto res = SqlToYql("USE plato;\n"
- "\n"
- "SELECT * FROM (SELECT 1 AS region_id)\n"
- "FLATTEN BY (\n"
- " CAST($unknown(region_id) AS List<String>) AS region\n"
- ")");
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:10: Error: Unknown name: $unknown\n");
- }
-
- Y_UNIT_TEST(EmptySymbolNameIsForbidden) {
- auto req = " $`` = 1; select $``;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:5: Error: Empty symbol name is not allowed\n");
- }
-
- Y_UNIT_TEST(WarnOnBinaryOpWithNullArg) {
- auto req = "select * from plato.Input where cast(key as Int32) != NULL";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Warning: Binary operation != will return NULL here, code: 4529\n");
-
- req = "select 1 or null";
- res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "");
- }
-
- Y_UNIT_TEST(ErrorIfTableSampleArgUsesColumns) {
- auto req = "SELECT key FROM plato.Input TABLESAMPLE BERNOULLI(MIN_OF(100.0, CAST(subkey as Int32)));";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:70: Error: Column reference \"subkey\" is not allowed in current scope\n");
- }
-
- Y_UNIT_TEST(DerivedColumnListForSelectIsNotSupportedYet) {
- auto req = "SELECT a,b,c FROM plato.Input as t(x,y,z);";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:35: Error: Derived column list is only supported for VALUES\n");
- }
-
- Y_UNIT_TEST(ErrorIfValuesHasDifferentCountOfColumns) {
- auto req = "VALUES (1,2,3), (4,5);";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: All VALUES items should have same size: expecting 3, got 2\n");
- }
-
- Y_UNIT_TEST(ErrorIfDerivedColumnSizeExceedValuesColumnCount) {
- auto req = "SELECT * FROM(VALUES (1,2), (3,4)) as t(x,y,z);";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: Derived column list size exceeds column count in VALUES\n");
- }
-
- Y_UNIT_TEST(WarnoOnAutogeneratedNamesForValues) {
- auto req = "PRAGMA WarnUnnamedColumns;\n"
- "SELECT * FROM (VALUES (1,2,3,4), (5,6,7,8)) as t(x,y);";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:16: Warning: Autogenerated column names column2...column3 will be used here, code: 4516\n");
- }
-
- Y_UNIT_TEST(ErrUnionAllWithOrderByWithoutExplicitLegacyMode) {
- auto req = "use plato;\n"
- "\n"
- "select * from Input order by key\n"
- "union all\n"
- "select * from Input order by key;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:21: Error: ORDER BY within UNION ALL is only allowed after last subquery\n");
- }
-
- Y_UNIT_TEST(ErrUnionAllWithLimitWithoutExplicitLegacyMode) {
- auto req = "use plato;\n"
- "\n"
- "select * from Input limit 10\n"
- "union all\n"
- "select * from Input limit 1;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:21: Error: LIMIT within UNION ALL is only allowed after last subquery\n");
- }
-
- Y_UNIT_TEST(ErrUnionAllWithIntoResultWithoutExplicitLegacyMode) {
- auto req = "use plato;\n"
- "\n"
- "select * from Input into result aaa\n"
- "union all\n"
- "select * from Input;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:21: Error: INTO RESULT within UNION ALL is only allowed after last subquery\n");
- }
-
- Y_UNIT_TEST(ErrUnionAllWithDiscardWithoutExplicitLegacyMode) {
- auto req = "use plato;\n"
- "\n"
- "select * from Input\n"
- "union all\n"
- "discard select * from Input;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:1: Error: DISCARD within UNION ALL is only allowed before first subquery\n");
- }
-
- Y_UNIT_TEST(ErrUnionAllKeepsIgnoredOrderByWarning) {
- auto req = "use plato;\n"
- "\n"
- "SELECT * FROM (\n"
- " SELECT * FROM Input\n"
- " UNION ALL\n"
- " SELECT t.* FROM Input AS t ORDER BY t.key\n"
- ");";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:3: Warning: ORDER BY without LIMIT in subquery will be ignored, code: 4504\n"
- "<main>:6:39: Error: Unknown correlation name: t\n");
- }
-
- Y_UNIT_TEST(ErrOrderByIgnoredButCheckedForMissingColumns) {
- auto req = "$src = SELECT key FROM (SELECT 1 as key, 2 as subkey) ORDER BY x; SELECT * FROM $src;";
- ExpectFailWithError(req, "<main>:1:8: Warning: ORDER BY without LIMIT in subquery will be ignored, code: 4504\n"
- "<main>:1:64: Error: Column x is not in source column set\n");
-
- req = "$src = SELECT key FROM plato.Input ORDER BY x; SELECT * FROM $src;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Warning: ORDER BY without LIMIT in subquery will be ignored, code: 4504\n");
- }
-
- Y_UNIT_TEST(InvalidTtlInterval) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (Key Uint32, CreatedAt Timestamp, PRIMARY KEY (Key))
- WITH (TTL = 1 On CreatedAt);
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:25: Error: Literal of Interval type is expected for TTL\n"
- "<main>:4:25: Error: Invalid TTL settings\n");
- }
-
- Y_UNIT_TEST(InvalidTtlUnit) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (Key Uint32, CreatedAt Uint32, PRIMARY KEY (Key))
- WITH (TTL = Interval("P1D") On CreatedAt AS PICOSECONDS);
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "mismatched input 'PICOSECONDS' expecting {MICROSECONDS, MILLISECONDS, NANOSECONDS, SECONDS}");
- }
-
- Y_UNIT_TEST(InvalidChangefeedSink) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (SINK_TYPE = "S3", MODE = "KEYS_ONLY", FORMAT = "json")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:55: Error: Unknown changefeed sink type: S3\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedSettings) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (SINK_TYPE = "local", FOO = "bar")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:64: Error: Unknown changefeed setting: FOO\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedInitialScan) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", INITIAL_SCAN = "foo")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:95: Error: Literal of Bool type is expected for INITIAL_SCAN\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedVirtualTimestamps) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", VIRTUAL_TIMESTAMPS = "foo")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:101: Error: Literal of Bool type is expected for VIRTUAL_TIMESTAMPS\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedResolvedTimestamps) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", BARRIERS_INTERVAL = "foo")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:100: Error: Literal of Interval type is expected for BARRIERS_INTERVAL\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedRetentionPeriod) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", RETENTION_PERIOD = "foo")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:99: Error: Literal of Interval type is expected for RETENTION_PERIOD\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedTopicPartitions) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", TOPIC_MIN_ACTIVE_PARTITIONS = "foo")
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:110: Error: Literal of integer type is expected for TOPIC_MIN_ACTIVE_PARTITIONS\n");
- }
-
- Y_UNIT_TEST(InvalidChangefeedAwsRegion) {
- auto req = R"(
- USE plato;
- CREATE TABLE tableName (
- Key Uint32, PRIMARY KEY (Key),
- CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", AWS_REGION = true)
- );
- )";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:93: Error: Literal of String type is expected for AWS_REGION\n");
- }
-
- Y_UNIT_TEST(ErrJoinWithGroupingSetsWithoutCorrelationName) {
- auto req = "USE plato;\n"
- "\n"
- "SELECT k1, k2, subkey\n"
- "FROM T1 AS a JOIN T2 AS b USING (key)\n"
- "GROUP BY GROUPING SETS(\n"
- " (a.key as k1, b.subkey as k2),\n"
- " (k1),\n"
- " (subkey)\n"
- ");";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:8:4: Error: Columns in grouping sets should have correlation name, error in key: subkey\n");
- }
-
- Y_UNIT_TEST(ErrJoinWithGroupByWithoutCorrelationName) {
- auto req = "USE plato;\n"
- "\n"
- "SELECT k1, k2,\n"
- " value\n"
- "FROM T1 AS a JOIN T2 AS b USING (key)\n"
- "GROUP BY a.key as k1, b.subkey as k2,\n"
- " value;";
- ExpectFailWithError(req,
- "<main>:7:5: Error: Columns in GROUP BY should have correlation name, error in key: value\n");
- }
-
- Y_UNIT_TEST(ErrWithMissingFrom) {
- auto req = "select 1 as key where 1 > 1;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:25: Error: Filtering is not allowed without FROM\n");
-
- req = "select 1 + count(*);";
- res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Aggregation is not allowed without FROM\n");
-
- req = "select 1 as key, subkey + value;";
- res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:18: Error: Column reference 'subkey'\n"
- "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:27: Error: Column reference 'value'\n");
-
- req = "select count(1) group by key;";
- res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:26: Error: Column reference 'key'\n");
- }
-
- Y_UNIT_TEST(ErrWithMissingFromForWindow) {
- auto req = "$c = () -> (1 + count(1) over w);\n"
- "select $c();";
- ExpectFailWithError(req,
- "<main>:1:9: Error: Window and aggregation functions are not allowed in this context\n"
- "<main>:1:17: Error: Failed to use aggregation function Count without window specification or in wrong place\n");
-
- req = "$c = () -> (1 + lead(1) over w);\n"
- "select $c();";
- ExpectFailWithError(req,
- "<main>:1:17: Error: Window functions are not allowed in this context\n"
- "<main>:1:17: Error: Failed to use window function Lead without window specification or in wrong place\n");
-
- req = "select 1 + count(1) over w window w as ();";
- ExpectFailWithError(req,
- "<main>:1:1: Error: Window and aggregation functions are not allowed without FROM\n"
- "<main>:1:12: Error: Failed to use aggregation function Count without window specification or in wrong place\n");
-
- req = "select 1 + lead(1) over w window w as ();";
- ExpectFailWithError(req,
- "<main>:1:12: Error: Window functions are not allowed without FROM\n"
- "<main>:1:12: Error: Failed to use window function Lead without window specification or in wrong place\n");
- }
-
- Y_UNIT_TEST(ErrWithMissingFromForInplaceWindow) {
- auto req = "$c = () -> (1 + count(1) over ());\n"
- "select $c();";
- ExpectFailWithError(req,
- "<main>:1:26: Error: Window and aggregation functions are not allowed in this context\n");
-
- req = "$c = () -> (1 + lead(1) over (rows between unbounded preceding and current row));\n"
- "select $c();";
- ExpectFailWithError(req,
- "<main>:1:25: Error: Window and aggregation functions are not allowed in this context\n");
-
- req = "select 1 + count(1) over ();";
- ExpectFailWithError(req,
- "<main>:1:1: Error: Window and aggregation functions are not allowed without FROM\n"
- "<main>:1:12: Error: Failed to use aggregation function Count without window specification or in wrong place\n");
-
- req = "select 1 + lead(1) over (rows between current row and unbounded following);";
- ExpectFailWithError(req,
- "<main>:1:12: Error: Window functions are not allowed without FROM\n"
- "<main>:1:12: Error: Failed to use window function Lead without window specification or in wrong place\n");
- }
-
- Y_UNIT_TEST(ErrDistinctInWrongPlace) {
- auto req = "select Some::Udf(distinct key) from plato.Input;";
- ExpectFailWithError(req,
- "<main>:1:18: Error: DISTINCT can only be used in aggregation functions\n");
- req = "select sum(key)(distinct foo) from plato.Input;";
- ExpectFailWithError(req,
- "<main>:1:17: Error: DISTINCT can only be used in aggregation functions\n");
-
- req = "select len(distinct foo) from plato.Input;";
- ExpectFailWithError(req,
- "<main>:1:8: Error: DISTINCT can only be used in aggregation functions\n");
-
- req = "$foo = ($x) -> ($x); select $foo(distinct key) from plato.Input;";
- ExpectFailWithError(req,
- "<main>:1:34: Error: DISTINCT can only be used in aggregation functions\n");
- }
-
- Y_UNIT_TEST(ErrForNotSingleChildInInlineAST) {
- ExpectFailWithError("select YQL::\"\"",
- "<main>:1:8: Error: Failed to parse YQL: expecting AST root node with single child, but got 0\n");
- ExpectFailWithError("select YQL::@@ \t@@",
- "<main>:1:8: Error: Failed to parse YQL: expecting AST root node with single child, but got 0\n");
- auto req = "$lambda = YQL::@@(lambda '(x)(+ x x)) (lambda '(y)(+ y y))@@;\n"
- "select ListMap([1, 2, 3], $lambda);";
- ExpectFailWithError(req,
- "<main>:1:11: Error: Failed to parse YQL: expecting AST root node with single child, but got 2\n");
- }
-
- Y_UNIT_TEST(ErrEmptyColumnName) {
- ExpectFailWithError("select * without \"\" from plato.Input",
- "<main>:1:18: Error: String literal can not be used here\n");
-
- ExpectFailWithError("select * without `` from plato.Input;",
- "<main>:1:18: Error: Empty column name is not allowed\n");
-
- ExpectFailWithErrorForAnsiLexer("select * without \"\" from plato.Input",
- "<main>:1:18: Error: Empty column name is not allowed\n");
-
- ExpectFailWithErrorForAnsiLexer("select * without `` from plato.Input;",
- "<main>:1:18: Error: Empty column name is not allowed\n");
- }
-
- Y_UNIT_TEST(ErrOnNonZeroArgumentsForTableRows) {
- ExpectFailWithError("$udf=\"\";process plato.Input using $udf(TableRows(k))",
- "<main>:1:40: Error: TableRows requires exactly 0 arguments\n");
- }
-
- Y_UNIT_TEST(ErrGroupByWithAggregationFunctionAndDistinctExpr) {
- ExpectFailWithError("select * from plato.Input group by count(distinct key|key)",
- "<main>:1:36: Error: Unable to GROUP BY aggregated values\n");
- }
-
- // FIXME: check if we can get old behaviour
-#if 0
- Y_UNIT_TEST(ErrWithSchemaWithColumnsWithoutType) {
- ExpectFailWithError("select * from plato.Input with COLUMNs",
- "<main>:1:32: Error: Expected type after COLUMNS\n"
- "<main>:1:32: Error: Failed to parse table hints\n");
-
- ExpectFailWithError("select * from plato.Input with scheMa",
- "<main>:1:32: Error: Expected type after SCHEMA\n"
- "<main>:1:32: Error: Failed to parse table hints\n");
- }
-#endif
-
- Y_UNIT_TEST(ErrCollectPreaggregatedInListLiteralWithoutFrom) {
- ExpectFailWithError("SELECT([VARIANCE(DISTINCT[])])",
- "<main>:1:1: Error: Column references are not allowed without FROM\n"
- "<main>:1:9: Error: Column reference '_yql_preagg_Variance0'\n");
- }
-
- Y_UNIT_TEST(ErrGroupBySmartParenAsTuple) {
- ExpectFailWithError("SELECT * FROM plato.Input GROUP BY (k, v,)",
- "<main>:1:41: Error: Unexpected trailing comma in grouping elements list\n");
- }
-
- Y_UNIT_TEST(HandleNestedSmartParensInGroupBy) {
- ExpectFailWithError("SELECT * FROM plato.Input GROUP BY (+() as k)",
- "<main>:1:37: Error: Unable to GROUP BY constant expression\n");
- }
-
- Y_UNIT_TEST(ErrRenameWithAddColumn) {
- ExpectFailWithError("USE plato; ALTER TABLE table RENAME TO moved, ADD COLUMN addc uint64",
- "<main>:1:40: Error: RENAME TO can not be used together with another table action\n");
- }
-
- Y_UNIT_TEST(ErrAddColumnAndRename) {
- // FIXME: fix positions in ALTER TABLE
- ExpectFailWithError("USE plato; ALTER TABLE table ADD COLUMN addc uint64, RENAME TO moved",
- "<main>:1:46: Error: RENAME TO can not be used together with another table action\n");
- }
-
- Y_UNIT_TEST(InvalidUuidValue) {
- ExpectFailWithError("SELECT Uuid('123e4567ae89ba12d3aa456a426614174ab0')",
- "<main>:1:8: Error: Invalid value \"123e4567ae89ba12d3aa456a426614174ab0\" for type Uuid\n");
- ExpectFailWithError("SELECT Uuid('123e4567ae89b-12d3-a456-426614174000')",
- "<main>:1:8: Error: Invalid value \"123e4567ae89b-12d3-a456-426614174000\" for type Uuid\n");
- }
-
- Y_UNIT_TEST(WindowFunctionWithoutOver) {
- ExpectFailWithError("SELECT LAST_VALUE(foo) FROM plato.Input",
- "<main>:1:8: Error: Can't use window function LastValue without window specification (OVER keyword is missing)\n");
- ExpectFailWithError("SELECT LAST_VALUE(foo) FROM plato.Input GROUP BY key",
- "<main>:1:8: Error: Can't use window function LastValue without window specification (OVER keyword is missing)\n");
- }
-
- Y_UNIT_TEST(CreateAlterUserWithoutCluster) {
- ExpectFailWithError("\n CREATE USER user ENCRYPTED PASSWORD 'foobar';", "<main>:2:2: Error: USE statement is missing - no default cluster is selected\n");
- ExpectFailWithError("ALTER USER CURRENT_USER RENAME TO $foo;", "<main>:1:1: Error: USE statement is missing - no default cluster is selected\n");
- }
-
- Y_UNIT_TEST(ModifyPermissionsWithoutCluster) {
- ExpectFailWithError("\n GRANT CONNECT ON `/Root` TO user;", "<main>:2:2: Error: USE statement is missing - no default cluster is selected\n");
- ExpectFailWithError("\n REVOKE MANAGE ON `/Root` FROM user;", "<main>:2:2: Error: USE statement is missing - no default cluster is selected\n");
- }
-
- Y_UNIT_TEST(ReservedRoleNames) {
- ExpectFailWithError("USE plato; CREATE USER current_User;", "<main>:1:24: Error: System role CURRENT_USER can not be used here\n");
- ExpectFailWithError("USE plato; ALTER USER current_User RENAME TO Current_role", "<main>:1:46: Error: System role CURRENT_ROLE can not be used here\n");
- UNIT_ASSERT(SqlToYql("USE plato; DROP GROUP IF EXISTS a, b, c, current_User;").IsOk());
- }
-
- Y_UNIT_TEST(DisableClassicDivisionWithError) {
- ExpectFailWithError("pragma ClassicDivision = 'false'; select $foo / 30;", "<main>:1:42: Error: Unknown name: $foo\n");
- }
-
- Y_UNIT_TEST(AggregationOfAgrregatedDistinctExpr) {
- ExpectFailWithError("select sum(sum(distinct x + 1)) from plato.Input", "<main>:1:12: Error: Aggregation of aggregated values is forbidden\n");
- }
-
- Y_UNIT_TEST(WarnForUnusedSqlHint) {
- NYql::TAstParseResult res = SqlToYql("select * from plato.Input1 as a join /*+ merge() */ plato.Input2 as b using(key);\n"
- "select --+ foo(bar)\n"
- " 1;");
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:23: Warning: Hint foo will not be used, code: 4534\n");
- }
-
- Y_UNIT_TEST(WarnForDeprecatedSchema) {
- NSQLTranslation::TTranslationSettings settings;
- settings.ClusterMapping["s3bucket"] = NYql::S3ProviderName;
- NYql::TAstParseResult res = SqlToYqlWithSettings("select * from s3bucket.`foo` with schema (col1 Int32, String as col2, Int64 as col3);", settings);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "Warning: Deprecated syntax for positional schema: please use 'column type' instead of 'type AS column', code: 4535\n");
- }
-
- Y_UNIT_TEST(ErrorOnColumnNameInMaxByLimit) {
- ExpectFailWithError(
- "SELECT AGGREGATE_BY(AsTuple(value, key), AggregationFactory(\"MAX_BY\", subkey)) FROM plato.Input;",
- "<main>:1:42: Error: Source does not allow column references\n"
- "<main>:1:71: Error: Column reference 'subkey'\n");
- }
-
- Y_UNIT_TEST(ErrorInLibraryWithTopLevelNamedSubquery) {
- TString withUnusedSubq = "$unused = select max(key) from plato.Input;\n"
- "\n"
- "define subquery $foo() as\n"
- " $count = select count(*) from plato.Input;\n"
- " select * from plato.Input limit $count / 2;\n"
- "end define;\n"
- "export $foo;\n";
- UNIT_ASSERT(SqlToYqlWithMode(withUnusedSubq, NSQLTranslation::ESqlMode::LIBRARY).IsOk());
-
- TString withTopLevelSubq = "$count = select count(*) from plato.Input;\n"
- "\n"
- "define subquery $foo() as\n"
- " select * from plato.Input limit $count / 2;\n"
- "end define;\n"
- "export $foo;\n";
- auto res = SqlToYqlWithMode(withTopLevelSubq, NSQLTranslation::ESqlMode::LIBRARY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: Named subquery can not be used as a top level statement in libraries\n");
- }
-
- Y_UNIT_TEST(SessionStartAndSessionStateShouldSurviveSessionWindowArgsError){
- TString query = R"(
- $init = ($_row) -> (min(1, 2)); -- error: aggregation func min() can not be used here
- $calculate = ($_row, $_state) -> (1);
- $update = ($_row, $_state) -> (2);
- SELECT
- SessionStart() over w as session_start,
- SessionState() over w as session_state,
- FROM plato.Input as t
- WINDOW w AS (
- PARTITION BY user, SessionWindow(ts + 1, $init, $update, $calculate)
- )
- )";
- ExpectFailWithError(query, "<main>:2:33: Error: Aggregation function Min requires exactly 1 argument(s), given: 2\n");
- }
-
- Y_UNIT_TEST(ScalarContextUsage1) {
- TString query = R"(
- $a = (select 1 as x, 2 as y);
- select 1 + $a;
- )";
- ExpectFailWithError(query, "<main>:2:39: Error: Source used in expression should contain one concrete column\n"
- "<main>:3:24: Error: Source is used here\n");
- }
-
- Y_UNIT_TEST(ScalarContextUsage2) {
- TString query = R"(
- use plato;
- $a = (select 1 as x, 2 as y);
- select * from concat($a);
- )";
- ExpectFailWithError(query, "<main>:3:39: Error: Source used in expression should contain one concrete column\n"
- "<main>:4:34: Error: Source is used here\n");
- }
-
- Y_UNIT_TEST(ScalarContextUsage3) {
- TString query = R"(
- use plato;
- $a = (select 1 as x, 2 as y);
- select * from range($a);
- )";
- ExpectFailWithError(query, "<main>:3:39: Error: Source used in expression should contain one concrete column\n"
- "<main>:4:33: Error: Source is used here\n");
- }
-
- Y_UNIT_TEST(ScalarContextUsage4) {
- TString query = R"(
- use plato;
- $a = (select 1 as x, 2 as y);
- insert into $a select 1;
- )";
- ExpectFailWithError(query, "<main>:3:39: Error: Source used in expression should contain one concrete column\n"
- "<main>:4:25: Error: Source is used here\n");
- }
-}
-
-void CheckUnused(const TString& req, const TString& symbol, unsigned row, unsigned col) {
- auto res = SqlToYql(req);
-
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), TStringBuilder() << "<main>:" << row << ":" << col << ": Warning: Symbol " << symbol << " is not used, code: 4527\n");
-}
-
-Y_UNIT_TEST_SUITE(WarnUnused) {
- Y_UNIT_TEST(ActionOrSubquery) {
- TString req = " $a()\n"
- "as select 1;\n"
- "end define;\n"
- "\n"
- "select 1;";
- CheckUnused("define action\n" + req, "$a", 2, 3);
- CheckUnused("define subquery\n" + req, "$a", 2, 3);
- }
-
- Y_UNIT_TEST(Import) {
- TString req = "import lib1 symbols\n"
- " $sqr;\n"
- "select 1;";
- CheckUnused(req, "$sqr", 2, 3);
-
- req = "import lib1 symbols\n"
- " $sqr as\n"
- " $sq;\n"
- "select 1;";
- CheckUnused(req, "$sq", 3, 5);
- }
-
- Y_UNIT_TEST(NamedNodeStatement) {
- TString req = " $a, $a = AsTuple(1, 2);\n"
- "select $a;";
- CheckUnused(req, "$a", 1, 2);
- req = "$a, $b = AsTuple(1, 2);\n"
- "select $a;";
- CheckUnused(req, "$b", 1, 6);
- CheckUnused(" $a = 1; $a = 2; select $a;", "$a", 1, 2);
- }
-
- Y_UNIT_TEST(Declare) {
- CheckUnused("declare $a as String;select 1;", "$a", 1, 9);
- }
-
- Y_UNIT_TEST(ActionParams) {
- TString req = "define action $a($x, $y) as\n"
- " select $x;\n"
- "end define;\n"
- "\n"
- "do $a(1,2);";
- CheckUnused(req, "$y", 1, 22);
- }
-
- Y_UNIT_TEST(SubqueryParams) {
- TString req = "use plato;\n"
- "define subquery $q($name, $x) as\n"
- " select * from $name;\n"
- "end define;\n"
- "\n"
- "select * from $q(\"Input\", 1);";
- CheckUnused(req, "$x", 2, 27);
- }
-
- Y_UNIT_TEST(For) {
- TString req = "define action $a() as\n"
- " select 1;\n"
- "end define;\n"
- "\n"
- "for $i in ListFromRange(1, 10)\n"
- "do $a();";
- CheckUnused(req, "$i", 5, 5);
- }
-
- Y_UNIT_TEST(LambdaParams) {
- TString req = "$lambda = ($x, $y) -> ($x);\n"
- "select $lambda(1, 2);";
- CheckUnused(req, "$y", 1, 16);
- }
-
- Y_UNIT_TEST(InsideLambdaBody) {
- TString req = "$lambda = () -> {\n"
- " $x = 1; return 1;\n"
- "};\n"
- "select $lambda();";
- CheckUnused(req, "$x", 2, 3);
- req = "$lambda = () -> {\n"
- " $x = 1; $x = 2; return $x;\n"
- "};\n"
- "select $lambda();";
- CheckUnused(req, "$x", 2, 3);
- }
-
- Y_UNIT_TEST(InsideAction) {
- TString req = "define action $a() as\n"
- " $x = 1; select 1;\n"
- "end define;\n"
- "\n"
- "do $a();";
- CheckUnused(req, "$x", 2, 3);
- req = "define action $a() as\n"
- " $x = 1; $x = 2; select $x;\n"
- "end define;\n"
- "\n"
- "do $a();";
- CheckUnused(req, "$x", 2, 3);
- }
-
- Y_UNIT_TEST(NoWarnOnNestedActions) {
- auto req = "pragma warning(\"error\", \"4527\");\n"
- "define action $action($b) as\n"
- " define action $aaa() as\n"
- " select $b;\n"
- " end define;\n"
- " do $aaa();\n"
- "end define;\n"
- "\n"
- "do $action(1);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(NoWarnForUsageAfterSubquery) {
- auto req = "use plato;\n"
- "pragma warning(\"error\", \"4527\");\n"
- "\n"
- "$a = 1;\n"
- "\n"
- "define subquery $q($table) as\n"
- " select * from $table;\n"
- "end define;\n"
- "\n"
- "select * from $q(\"Input\");\n"
- "select $a;";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-}
-
-Y_UNIT_TEST_SUITE(AnonymousNames) {
- Y_UNIT_TEST(ReferenceAnonymousVariableIsForbidden) {
- auto req = "$_ = 1; select $_;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:16: Error: Unable to reference anonymous name $_\n");
-
- req = "$`_` = 1; select $`_`;";
- res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:18: Error: Unable to reference anonymous name $_\n");
- }
-
- Y_UNIT_TEST(Declare) {
- auto req = "declare $_ as String;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:9: Error: Can not use anonymous name '$_' in DECLARE statement\n");
- }
-
- Y_UNIT_TEST(ActionSubquery) {
- auto req = "define action $_() as select 1; end define;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:15: Error: Can not use anonymous name '$_' as ACTION name\n");
-
- req = "define subquery $_() as select 1; end define;";
- res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: Can not use anonymous name '$_' as SUBQUERY name\n");
- }
-
- Y_UNIT_TEST(Import) {
- auto req = "import lib symbols $sqr as $_;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:28: Error: Can not import anonymous name $_\n");
- }
-
- Y_UNIT_TEST(Export) {
- auto req = "export $_;";
- auto res = SqlToYqlWithMode(req, NSQLTranslation::ESqlMode::LIBRARY);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Can not export anonymous name $_\n");
- }
-
- Y_UNIT_TEST(AnonymousInActionArgs) {
- auto req = "pragma warning(\"error\", \"4527\");\n"
- "define action $a($_, $y, $_) as\n"
- " select $y;\n"
- "end define;\n"
- "\n"
- "do $a(1,2,3);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(AnonymousInSubqueryArgs) {
- auto req = "use plato;\n"
- "pragma warning(\"error\", \"4527\");\n"
- "define subquery $q($_, $y, $_) as\n"
- " select * from $y;\n"
- "end define;\n"
- "\n"
- "select * from $q(1,\"Input\",3);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(AnonymousInLambdaArgs) {
- auto req = "pragma warning(\"error\", \"4527\");\n"
- "$lambda = ($_, $x, $_) -> ($x);\n"
- "select $lambda(1,2,3);";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(AnonymousInFor) {
- auto req = "pragma warning(\"error\", \"4527\");\n"
- "evaluate for $_ in ListFromRange(1, 10) do begin select 1; end do;";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-
- Y_UNIT_TEST(Assignment) {
- auto req = "pragma warning(\"error\", \"4527\");\n"
- "$_ = 1;\n"
- "$_, $x, $_ = AsTuple(1,2,3);\n"
- "select $x;";
- UNIT_ASSERT(SqlToYql(req).IsOk());
- }
-}
-
-Y_UNIT_TEST_SUITE(JsonValue) {
- Y_UNIT_TEST(JsonValueArgumentCount) {
- NYql::TAstParseResult res = SqlToYql("select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json));");
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Error: mismatched input ')' expecting ','\n");
- }
-
- Y_UNIT_TEST(JsonValueJsonPathMustBeLiteralString) {
- NYql::TAstParseResult res = SqlToYql("$jsonPath = \"strict $.key\"; select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), $jsonPath);");
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:79: Error: mismatched input '$' expecting STRING_VALUE\n");
- }
-
- Y_UNIT_TEST(JsonValueTranslation) {
- NYql::TAstParseResult res = SqlToYql("select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\");");
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"strict $.key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("SafeCast"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("DataType 'Json"));
- };
-
- TWordCountHive elementStat({"JsonValue"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["JsonValue"]);
- }
-
- Y_UNIT_TEST(JsonValueReturningSection) {
- for (const auto& typeName : {"Bool", "Int64", "Double", "String"}) {
- NYql::TAstParseResult res = SqlToYql(
- TStringBuilder() << "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" RETURNING " << typeName << ");"
- );
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"strict $.key\""));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("SafeCast"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("DataType 'Json"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(TStringBuilder() << "DataType '" << typeName));
- };
-
- TWordCountHive elementStat({typeName});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat[typeName] > 0);
- }
- }
-
- Y_UNIT_TEST(JsonValueInvalidReturningType) {
- NYql::TAstParseResult res = SqlToYql("select JSON_VALUE(CAST(@@{'key': 1238}@@ as Json), 'strict $.key' RETURNING invalid);");
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:77: Error: Unknown simple type 'invalid'\n");
- }
-
- Y_UNIT_TEST(JsonValueAndReturningInExpressions) {
- NYql::TAstParseResult res = SqlToYql(
- "USE plato\n;"
- "$json_value = \"some string\";\n"
- "SELECT $json_value;\n"
- "SELECT 1 as json_value;\n"
- "SELECT $json_value as json_value;\n"
- "$returning = \"another string\";\n"
- "SELECT $returning;\n"
- "SELECT 1 as returning;\n"
- "SELECT $returning as returning;\n"
- );
-
- UNIT_ASSERT(res.Root);
- }
-
- Y_UNIT_TEST(JsonValueValidCaseHandlers) {
- const TVector<std::pair<TString, TString>> testCases = {
- {"", "'DefaultValue (Null)"},
- {"NULL", "'DefaultValue (Null)"},
- {"ERROR", "'Error (Null)"},
- {"DEFAULT 123", "'DefaultValue (Int32 '\"123\")"},
- };
-
- for (const auto& onEmpty : testCases) {
- for (const auto& onError : testCases) {
- TStringBuilder query;
- query << "$json = CAST(@@{\"key\": 1238}@@ as Json);\n"
- << "SELECT JSON_VALUE($json, \"strict $.key\"";
- if (!onEmpty.first.empty()) {
- query << " " << onEmpty.first << " ON EMPTY";
- }
- if (!onError.first.empty()) {
- query << " " << onError.first << " ON ERROR";
- }
- query << ");\n";
-
- NYql::TAstParseResult res = SqlToYql(query);
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(onEmpty.second + " " + onError.second));
- };
-
- TWordCountHive elementStat({"JsonValue"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonValue"] > 0);
- }
- }
- }
-
- Y_UNIT_TEST(JsonValueTooManyCaseHandlers) {
- NYql::TAstParseResult res = SqlToYql(
- "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" NULL ON EMPTY NULL ON ERROR NULL ON EMPTY);\n"
- );
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(
- Err2Str(res),
- "<main>:1:52: Error: Only 1 ON EMPTY and/or 1 ON ERROR clause is expected\n"
- );
- }
-
- Y_UNIT_TEST(JsonValueTooManyOnEmpty) {
- NYql::TAstParseResult res = SqlToYql(
- "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" NULL ON EMPTY NULL ON EMPTY);\n"
- );
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(
- Err2Str(res),
- "<main>:1:52: Error: Only 1 ON EMPTY clause is expected\n"
- );
- }
-
- Y_UNIT_TEST(JsonValueTooManyOnError) {
- NYql::TAstParseResult res = SqlToYql(
- "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" NULL ON ERROR NULL ON ERROR);\n"
- );
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(
- Err2Str(res),
- "<main>:1:52: Error: Only 1 ON ERROR clause is expected\n"
- );
- }
-
- Y_UNIT_TEST(JsonValueOnEmptyAfterOnError) {
- NYql::TAstParseResult res = SqlToYql(
- "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" NULL ON ERROR NULL ON EMPTY);\n"
- );
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(
- Err2Str(res),
- "<main>:1:52: Error: ON EMPTY clause must be before ON ERROR clause\n"
- );
- }
-
- Y_UNIT_TEST(JsonValueNullInput) {
- NYql::TAstParseResult res = SqlToYql(R"(SELECT JSON_VALUE(NULL, "strict $.key");)");
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Nothing (OptionalType (DataType 'Json)))"));
- };
-
- TWordCountHive elementStat({"JsonValue"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonValue"] > 0);
- }
-}
-
-Y_UNIT_TEST_SUITE(JsonExists) {
- Y_UNIT_TEST(JsonExistsValidHandlers) {
- const TVector<std::pair<TString, TString>> testCases = {
- {"", "(Just (Bool '\"false\"))"},
- {"TRUE ON ERROR", "(Just (Bool '\"true\"))"},
- {"FALSE ON ERROR", "(Just (Bool '\"false\"))"},
- {"UNKNOWN ON ERROR", "(Nothing (OptionalType (DataType 'Bool)))"},
- // NOTE: in this case we expect arguments of JsonExists callable to end immediately
- // after variables. This parenthesis at the end of the expression is left on purpose
- {"ERROR ON ERROR", "(Utf8 '\"strict $.key\") (JsonVariables))"},
- };
-
- for (const auto& item : testCases) {
- NYql::TAstParseResult res = SqlToYql(
- TStringBuilder() << R"(
- $json = CAST(@@{"key": 1238}@@ as Json);
- SELECT JSON_EXISTS($json, "strict $.key" )" << item.first << ");\n"
- );
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(item.second));
- };
-
- TWordCountHive elementStat({"JsonExists"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonExists"] > 0);
- }
- }
-
- Y_UNIT_TEST(JsonExistsInvalidHandler) {
- NYql::TAstParseResult res = SqlToYql(R"(
- $json = CAST(@@{"key": 1238}@@ as Json);
- $default = false;
- SELECT JSON_EXISTS($json, "strict $.key" $default ON ERROR);
- )");
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:53: Error: mismatched input '$' expecting {')', ERROR, FALSE, TRUE, UNKNOWN}\n");
- }
-
- Y_UNIT_TEST(JsonExistsNullInput) {
- NYql::TAstParseResult res = SqlToYql(R"(SELECT JSON_EXISTS(NULL, "strict $.key");)");
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Nothing (OptionalType (DataType 'Json)))"));
- };
-
- TWordCountHive elementStat({"JsonExists"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonExists"] > 0);
- }
-}
-
-Y_UNIT_TEST_SUITE(JsonQuery) {
- Y_UNIT_TEST(JsonQueryValidHandlers) {
- using TTestSuite = const TVector<std::pair<TString, TString>>;
- TTestSuite wrapCases = {
- {"", "'NoWrap"},
- {"WITHOUT WRAPPER", "'NoWrap"},
- {"WITHOUT ARRAY WRAPPER", "'NoWrap"},
- {"WITH WRAPPER", "'Wrap"},
- {"WITH ARRAY WRAPPER", "'Wrap"},
- {"WITH UNCONDITIONAL WRAPPER", "'Wrap"},
- {"WITH UNCONDITIONAL ARRAY WRAPPER", "'Wrap"},
- {"WITH CONDITIONAL WRAPPER", "'ConditionalWrap"},
- {"WITH CONDITIONAL ARRAY WRAPPER", "'ConditionalWrap"},
- };
- TTestSuite handlerCases = {
- {"", "'Null"},
- {"ERROR", "'Error"},
- {"NULL", "'Null"},
- {"EMPTY ARRAY", "'EmptyArray"},
- {"EMPTY OBJECT", "'EmptyObject"},
- };
-
- for (const auto& wrap : wrapCases) {
- for (const auto& onError : handlerCases) {
- for (const auto& onEmpty : handlerCases) {
- TStringBuilder query;
- query << R"($json = CAST(@@{"key": [123]}@@ as Json);
- SELECT JSON_QUERY($json, "strict $.key" )" << wrap.first;
- if (!onEmpty.first.empty()) {
- if (wrap.first.StartsWith("WITH ")) {
- continue;
- }
- query << " " << onEmpty.first << " ON EMPTY";
- }
- if (!onError.first.empty()) {
- query << " " << onError.first << " ON ERROR";
- }
- query << ");\n";
-
- NYql::TAstParseResult res = SqlToYql(query);
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- const TString args = TStringBuilder() << wrap.second << " " << onEmpty.second << " " << onError.second;
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(args));
- };
-
- Cout << wrap.first << " " << onEmpty.first << " " << onError.first << Endl;
-
- TWordCountHive elementStat({"JsonQuery"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonQuery"] > 0);
- }
- }
- }
- }
-
- Y_UNIT_TEST(JsonQueryOnEmptyWithWrapper) {
- NYql::TAstParseResult res = SqlToYql(R"(
- $json = CAST(@@{"key": 1238}@@ as Json);
- SELECT JSON_QUERY($json, "strict $" WITH ARRAY WRAPPER EMPTY ARRAY ON EMPTY);
- )");
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:38: Error: ON EMPTY is prohibited because WRAPPER clause is specified\n");
- }
-
- Y_UNIT_TEST(JsonQueryNullInput) {
- NYql::TAstParseResult res = SqlToYql(R"(SELECT JSON_QUERY(NULL, "strict $.key");)");
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Nothing (OptionalType (DataType 'Json)))"));
- };
-
- TWordCountHive elementStat({"JsonQuery"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonQuery"] > 0);
- }
-}
-
-Y_UNIT_TEST_SUITE(JsonPassing) {
- Y_UNIT_TEST(SupportedVariableTypes) {
- const TVector<TString> functions = {"JSON_EXISTS", "JSON_VALUE", "JSON_QUERY"};
-
- for (const auto& function : functions) {
- const auto query = Sprintf(R"(
- pragma CompactNamedExprs;
- $json = CAST(@@{"key": 1238}@@ as Json);
- SELECT %s(
- $json,
- "strict $.key"
- PASSING
- "string" as var1,
- 1.234 as var2,
- CAST(1 as Int64) as var3,
- true as var4,
- $json as var5
- ))",
- function.data()
- );
- NYql::TAstParseResult res = SqlToYql(query);
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var1" (String '"string")))"), "Cannot find `var1`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var2" (Double '"1.234")))"), "Cannot find `var2`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var3" (SafeCast (Int32 '"1") (DataType 'Int64))))"), "Cannot find `var3`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var4" (Bool '"true")))"), "Cannot find `var4`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var5" namedexprnode0))"), "Cannot find `var5`");
- };
-
- TWordCountHive elementStat({"JsonVariables"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonVariables"] > 0);
- }
- }
-
- Y_UNIT_TEST(ValidVariableNames) {
- const TVector<TString> functions = {"JSON_EXISTS", "JSON_VALUE", "JSON_QUERY"};
-
- for (const auto& function : functions) {
- const auto query = Sprintf(R"(
- $json = CAST(@@{"key": 1238}@@ as Json);
- SELECT %s(
- $json,
- "strict $.key"
- PASSING
- "one" as var1,
- "two" as "VaR2",
- "three" as `var3`,
- "four" as VaR4
- ))",
- function.data()
- );
- NYql::TAstParseResult res = SqlToYql(query);
-
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var1" (String '"one")))"), "Cannot find `var1`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"VaR2" (String '"two")))"), "Cannot find `VaR2`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var3" (String '"three")))"), "Cannot find `var3`");
- UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"VaR4" (String '"four")))"), "Cannot find `VaR4`");
- };
-
- TWordCountHive elementStat({"JsonVariables"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["JsonVariables"] > 0);
- }
- }
-}
-
-Y_UNIT_TEST_SUITE(MigrationToJsonApi) {
- Y_UNIT_TEST(WarningOnDeprecatedJsonUdf) {
- NYql::TAstParseResult res = SqlToYql(R"(
- $json = CAST(@@{"key": 1234}@@ as Json);
- SELECT Json::Parse($json);
- )");
-
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:26: Warning: Json UDF is deprecated. Please use JSON API instead, code: 4506\n");
- }
-}
-
-Y_UNIT_TEST_SUITE(AnsiIdentsNegative) {
- Y_UNIT_TEST(EnableAnsiLexerFromRequestSpecialComments) {
- auto req = "\n"
- "\t --!ansi_lexer \n"
- "-- Some comment\n"
- "-- another comment\n"
- "pragma SimpleColumns;\n"
- "\n"
- "select 1, '''' as empty;";
-
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(AnsiLexerShouldNotBeEnabledHere) {
- auto req = "$str = '\n"
- "--!ansi_lexer\n"
- "--!syntax_v1\n"
- "';\n"
- "\n"
- "select 1, $str, \"\" as empty;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(DoubleQuotesInDictsTuplesOrLists) {
- auto req = "$d = { 'a': 1, \"b\": 2, 'c': 3,};";
-
- auto res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:16: Error: Column reference \"b\" is not allowed in current scope\n");
-
- req = "$t = (1, 2, \"a\");";
-
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:13: Error: Column reference \"a\" is not allowed in current scope\n");
-
- req = "$l = ['a', 'b', \"c\"];";
-
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: Column reference \"c\" is not allowed in current scope\n");
- }
-
- Y_UNIT_TEST(MultilineComments) {
- auto req = "/*/**/ select 1;";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- req = "/*\n"
- "--/*\n"
- "*/ select 1;";
- res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- req = "/*\n"
- "/*\n"
- "--*/\n"
- "*/ select 1;";
- res = SqlToYql(req);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:0: Error: mismatched input '*' expecting {';', '(', '$', ALTER, ANALYZE, BACKUP, BATCH, COMMIT, CREATE, DECLARE, DEFINE, DELETE, DISCARD, DO, DROP, EVALUATE, EXPLAIN, EXPORT, FOR, FROM, GRANT, IF, IMPORT, INSERT, PARALLEL, PRAGMA, PROCESS, REDUCE, REPLACE, RESTORE, REVOKE, ROLLBACK, SELECT, SHOW, UPDATE, UPSERT, USE, VALUES}\n");
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(res.Root);
- }
-}
-
-Y_UNIT_TEST_SUITE(AnsiOptionalAs) {
- Y_UNIT_TEST(OptionalAsInProjection) {
- UNIT_ASSERT(SqlToYql("PRAGMA AnsiOptionalAs; SELECT a b, c FROM plato.Input;").IsOk());
- ExpectFailWithError("PRAGMA DisableAnsiOptionalAs;\n"
- "SELECT a b, c FROM plato.Input;",
- "<main>:2:10: Error: Expecting mandatory AS here. Did you miss comma? Please add PRAGMA AnsiOptionalAs; for ANSI compatibility\n");
- }
-
- Y_UNIT_TEST(OptionalAsWithKeywords) {
- UNIT_ASSERT(SqlToYql("PRAGMA AnsiOptionalAs; SELECT a type, b data, c source FROM plato.Input;").IsOk());
- }
-}
-
-Y_UNIT_TEST_SUITE(SessionWindowNegative) {
- Y_UNIT_TEST(SessionWindowWithoutSource) {
- ExpectFailWithError("SELECT 1 + SessionWindow(ts, 32);",
- "<main>:1:12: Error: SessionWindow requires data source\n");
- }
-
- Y_UNIT_TEST(SessionWindowInProjection) {
- ExpectFailWithError("SELECT 1 + SessionWindow(ts, 32) from plato.Input;",
- "<main>:1:12: Error: SessionWindow can only be used as a top-level GROUP BY / PARTITION BY expression\n");
- }
-
- Y_UNIT_TEST(SessionWindowWithNonConstSecondArg) {
- ExpectFailWithError(
- "SELECT key, session_start FROM plato.Input\n"
- "GROUP BY SessionWindow(ts, 32 + subkey) as session_start, key;",
-
- "<main>:2:10: Error: Source does not allow column references\n"
- "<main>:2:33: Error: Column reference 'subkey'\n");
- }
-
- Y_UNIT_TEST(SessionWindowWithWrongNumberOfArgs) {
- ExpectFailWithError("SELECT * FROM plato.Input GROUP BY SessionWindow()",
- "<main>:1:36: Error: SessionWindow requires either two or four arguments\n");
- ExpectFailWithError("SELECT * FROM plato.Input GROUP BY SessionWindow(key, subkey, 100)",
- "<main>:1:36: Error: SessionWindow requires either two or four arguments\n");
- }
-
- Y_UNIT_TEST(DuplicateSessionWindow) {
- ExpectFailWithError(
- "SELECT\n"
- " *\n"
- "FROM plato.Input\n"
- "GROUP BY\n"
- " SessionWindow(ts, 10),\n"
- " user,\n"
- " SessionWindow(ts, 20)\n"
- ";",
-
- "<main>:7:5: Error: Duplicate session window specification:\n"
- "<main>:5:5: Error: Previous session window is declared here\n");
-
- ExpectFailWithError(
- "SELECT\n"
- " MIN(key) over w\n"
- "FROM plato.Input\n"
- "WINDOW w AS (\n"
- " PARTITION BY SessionWindow(ts, 10), user,\n"
- " SessionWindow(ts, 20)\n"
- ");",
-
- "<main>:6:5: Error: Duplicate session window specification:\n"
- "<main>:5:18: Error: Previous session window is declared here\n");
- }
-
- Y_UNIT_TEST(SessionStartStateWithoutSource) {
- ExpectFailWithError("SELECT 1 + SessionStart();",
- "<main>:1:12: Error: SessionStart requires data source\n");
- ExpectFailWithError("SELECT 1 + SessionState();",
- "<main>:1:12: Error: SessionState requires data source\n");
- }
-
- Y_UNIT_TEST(SessionStartStateWithoutGroupByOrWindow) {
- ExpectFailWithError("SELECT 1 + SessionStart() from plato.Input;",
- "<main>:1:12: Error: SessionStart can not be used without aggregation by SessionWindow\n");
- ExpectFailWithError("SELECT 1 + SessionState() from plato.Input;",
- "<main>:1:12: Error: SessionState can not be used without aggregation by SessionWindow\n");
- }
-
- Y_UNIT_TEST(SessionStartStateWithGroupByWithoutSession) {
- ExpectFailWithError("SELECT 1 + SessionStart() from plato.Input group by user;",
- "<main>:1:12: Error: SessionStart can not be used here: SessionWindow specification is missing in GROUP BY\n");
- ExpectFailWithError("SELECT 1 + SessionState() from plato.Input group by user;",
- "<main>:1:12: Error: SessionState can not be used here: SessionWindow specification is missing in GROUP BY\n");
- }
-
- Y_UNIT_TEST(SessionStartStateWithoutOverWithWindowWithoutSession) {
- ExpectFailWithError("SELECT 1 + SessionStart(), MIN(key) over w from plato.Input window w as ()",
- "<main>:1:12: Error: SessionStart can not be used without aggregation by SessionWindow. Maybe you forgot to add OVER `window_name`?\n");
- ExpectFailWithError("SELECT 1 + SessionState(), MIN(key) over w from plato.Input window w as ()",
- "<main>:1:12: Error: SessionState can not be used without aggregation by SessionWindow. Maybe you forgot to add OVER `window_name`?\n");
- }
-
- Y_UNIT_TEST(SessionStartStateWithWindowWithoutSession) {
- ExpectFailWithError("SELECT 1 + SessionStart() over w, MIN(key) over w from plato.Input window w as ()",
- "<main>:1:12: Error: SessionStart can not be used with window w: SessionWindow specification is missing in PARTITION BY\n");
- ExpectFailWithError("SELECT 1 + SessionState() over w, MIN(key) over w from plato.Input window w as ()",
- "<main>:1:12: Error: SessionState can not be used with window w: SessionWindow specification is missing in PARTITION BY\n");
- }
-
- Y_UNIT_TEST(SessionStartStateWithSessionedWindow) {
- ExpectFailWithError("SELECT 1 + SessionStart(), MIN(key) over w from plato.Input group by key window w as (partition by SessionWindow(ts, 1)) ",
- "<main>:1:12: Error: SessionStart can not be used here: SessionWindow specification is missing in GROUP BY. Maybe you forgot to add OVER `window_name`?\n");
- ExpectFailWithError("SELECT 1 + SessionState(), MIN(key) over w from plato.Input group by key window w as (partition by SessionWindow(ts, 1)) ",
- "<main>:1:12: Error: SessionState can not be used here: SessionWindow specification is missing in GROUP BY. Maybe you forgot to add OVER `window_name`?\n");
- }
-
- Y_UNIT_TEST(AggregationBySessionStateIsNotSupportedYet) {
- ExpectFailWithError("SELECT SOME(1 + SessionState()), key from plato.Input group by key, SessionWindow(ts, 1);",
- "<main>:1:17: Error: SessionState with GROUP BY is not supported yet\n");
- }
-
- Y_UNIT_TEST(SessionWindowInRtmr) {
- NYql::TAstParseResult res = SqlToYql(
- "SELECT * FROM plato.Input GROUP BY SessionWindow(ts, 10);",
- 10, TString(NYql::RtmrProviderName));
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:54: Error: Streaming group by query must have a hopping window specification.\n");
-
- res = SqlToYql(R"(
- SELECT key, SUM(value) AS value FROM plato.Input
- GROUP BY key, HOP(subkey, "PT10S", "PT30S", "PT20S"), SessionWindow(ts, 10);
- )", 10, TString(NYql::RtmrProviderName));
-
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:13: Error: SessionWindow is unsupported for streaming sources\n");
- }
-}
-
-Y_UNIT_TEST_SUITE(LibraSqlSugar) {
- auto makeResult = [](TStringBuf settings) {
- return SqlToYql(
- TStringBuilder()
- << settings
- << "\n$udf1 = MyLibra::MakeLibraPreprocessor($settings);"
- << "\n$udf2 = CustomLibra::MakeLibraPreprocessor($settings);"
- << "\nPROCESS plato.Input USING $udf1(TableRow())"
- << "\nUNION ALL"
- << "\nPROCESS plato.Input USING $udf2(TableRow());"
- );
- };
-
- Y_UNIT_TEST(EmptySettings) {
- auto res = makeResult(R"(
- $settings = AsStruct();
- )");
- UNIT_ASSERT(res.IsOk());
- }
-
- Y_UNIT_TEST(OnlyEntities) {
- auto res = makeResult(R"(
- $settings = AsStruct(
- AsList("A", "B", "C") AS Entities
- );
- )");
- UNIT_ASSERT(res.IsOk());
- }
-
- Y_UNIT_TEST(EntitiesWithStrategy) {
- auto res = makeResult(R"(
- $settings = AsStruct(
- AsList("A", "B", "C") AS Entities,
- "blacklist" AS EntitiesStrategy
- );
- )");
- UNIT_ASSERT(res.IsOk());
- }
-
- Y_UNIT_TEST(AllSettings) {
- auto res = makeResult(R"(
- $settings = AsStruct(
- AsList("A", "B", "C") AS Entities,
- "whitelist" AS EntitiesStrategy,
- "path" AS BlockstatDict,
- false AS ParseWithFat,
- "map" AS Mode
- );
- )");
- UNIT_ASSERT(res.IsOk());
- }
-
- Y_UNIT_TEST(BadStrategy) {
- auto res = makeResult(R"(
- $settings = AsStruct("bad" AS EntitiesStrategy);
- )");
- UNIT_ASSERT_STRING_CONTAINS(
- Err2Str(res),
- "Error: MakeLibraPreprocessor got invalid entities strategy: expected 'whitelist' or 'blacklist'"
- );
- }
-
- Y_UNIT_TEST(BadEntities) {
- auto res = makeResult(R"(
- $settings = AsStruct(AsList("A", 1) AS Entities);
- )");
- UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "Error: MakeLibraPreprocessor entity must be string literal");
- }
-}
-
-Y_UNIT_TEST_SUITE(TrailingQuestionsNegative) {
- Y_UNIT_TEST(Basic) {
- ExpectFailWithError("SELECT 1?;", "<main>:1:9: Error: Unexpected token '?' at the end of expression\n");
- ExpectFailWithError("SELECT 1? + 1;", "<main>:1:10: Error: mismatched input '+' expecting {<EOF>, ';'}\n");
- ExpectFailWithError("SELECT 1 + 1??? < 2", "<main>:1:13: Error: Unexpected token '?' at the end of expression\n");
- ExpectFailWithError("SELECT 1? > 2? > 3?",
- "<main>:1:11: Error: Unexpected token '?' at the end of expression\n"
- "<main>:1:16: Error: Unexpected token '?' at the end of expression\n"
- "<main>:1:21: Error: Unexpected token '?' at the end of expression\n");
- }
-
- Y_UNIT_TEST(SmartParen) {
- ExpectFailWithError("$x = 1; SELECT (Int32?, $x?)", "<main>:1:27: Error: Unexpected token '?' at the end of expression\n");
- ExpectFailWithError("SELECT (Int32, foo?)", "<main>:1:19: Error: Unexpected token '?' at the end of expression\n");
- }
-
- Y_UNIT_TEST(LambdaOptArgs) {
- ExpectFailWithError("$l = ($x, $y?, $z??, $t?) -> ($x);", "<main>:1:18: Error: Expecting at most one '?' token here (for optional lambda parameters), but got 2\n");
- }
-}
-
-Y_UNIT_TEST_SUITE(FlexibleTypes) {
- Y_UNIT_TEST(AssumeOrderByType) {
- UNIT_ASSERT(SqlToYql("PRAGMA FlexibleTypes; SELECT 1 AS int32 ASSUME ORDER BY int32").IsOk());
- }
-
- Y_UNIT_TEST(GroupingSets) {
- UNIT_ASSERT(SqlToYql("PRAGMA FlexibleTypes; SELECT COUNT(*) AS cnt, text, uuid FROM plato.Input GROUP BY GROUPING SETS((uuid), (uuid, text));").IsOk());
- }
-
- Y_UNIT_TEST(WeakField) {
- UNIT_ASSERT(SqlToYql("PRAGMA FlexibleTypes; SELECT WeakField(text, string) as text FROM plato.Input").IsOk());
- }
-
- Y_UNIT_TEST(Aggregation1) {
- TString q =
- "PRAGMA FlexibleTypes;\n"
- "$foo = ($x, $const, $type) -> ($x || $const || FormatType($type));\n"
- "SELECT $foo(SOME(x), 'aaa', String) FROM plato.Input GROUP BY y;";
- UNIT_ASSERT(SqlToYql(q).IsOk());
- }
-
- Y_UNIT_TEST(Aggregation2) {
- TString q =
- "PRAGMA FlexibleTypes;\n"
- "SELECT 1 + String + MAX(key) FROM plato.Input;";
- UNIT_ASSERT(SqlToYql(q).IsOk());
- }
-}
-
-Y_UNIT_TEST_SUITE(ExternalDeclares) {
- Y_UNIT_TEST(BasicUsage) {
- NSQLTranslation::TTranslationSettings settings;
- settings.DeclaredNamedExprs["foo"] = "String";
- auto res = SqlToYqlWithSettings("select $foo;", settings);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "declare") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"__((declare "$foo" (DataType 'String)))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("declare"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["declare"]);
- }
-
- Y_UNIT_TEST(DeclareOverrides) {
- NSQLTranslation::TTranslationSettings settings;
- settings.DeclaredNamedExprs["foo"] = "String";
- auto res = SqlToYqlWithSettings("declare $foo as Int32; select $foo;", settings);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "declare") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"__((declare "$foo" (DataType 'Int32)))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("declare"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["declare"]);
- }
-
- Y_UNIT_TEST(UnusedDeclareDoesNotProduceWarning) {
- NSQLTranslation::TTranslationSettings settings;
- settings.DeclaredNamedExprs["foo"] = "String";
- auto res = SqlToYqlWithSettings("select 1;", settings);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "declare") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"__((declare "$foo" (DataType 'String)))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("declare"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["declare"]);
- }
-
- Y_UNIT_TEST(DeclaresWithInvalidTypesFails) {
- NSQLTranslation::TTranslationSettings settings;
- settings.DeclaredNamedExprs["foo"] = "List<BadType>";
- auto res = SqlToYqlWithSettings("select 1;", settings);
- UNIT_ASSERT(!res.Root);
- UNIT_ASSERT_NO_DIFF(Err2Str(res),
- "<main>:0:5: Error: Unknown type: 'BadType'\n"
- "<main>: Error: Failed to parse type for externally declared name 'foo'\n");
- }
-}
-
-Y_UNIT_TEST_SUITE(ExternalDataSource) {
- Y_UNIT_TEST(CreateExternalDataSourceWithAuthNone) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="NONE"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"NONE") '('"location" '"my-bucket") '('"source_type" '"ObjectStorage"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithAuthServiceAccount) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="SERVICE_ACCOUNT",
- SERVICE_ACCOUNT_ID="sa",
- SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"SERVICE_ACCOUNT") '('"location" '"my-bucket") '('"service_account_id" '"sa") '('"service_account_secret_name" '"sa_secret_name") '('"source_type" '"ObjectStorage"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithBasic) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="BASIC",
- LOGIN="admin",
- PASSWORD_SECRET_NAME="secret_name"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"BASIC") '('"location" '"protocol://host:port/") '('"login" '"admin") '('"password_secret_name" '"secret_name") '('"source_type" '"PostgreSQL"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithMdbBasic) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="MDB_BASIC",
- SERVICE_ACCOUNT_ID="sa",
- SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name",
- LOGIN="admin",
- PASSWORD_SECRET_NAME="secret_name"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"MDB_BASIC") '('"location" '"protocol://host:port/") '('"login" '"admin") '('"password_secret_name" '"secret_name") '('"service_account_id" '"sa") '('"service_account_secret_name" '"sa_secret_name") '('"source_type" '"PostgreSQL"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithAws) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="AWS",
- AWS_ACCESS_KEY_ID_SECRET_NAME="secred_id_name",
- AWS_SECRET_ACCESS_KEY_SECRET_NAME="secret_key_name",
- AWS_REGION="ru-central-1"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"AWS") '('"aws_access_key_id_secret_name" '"secred_id_name") '('"aws_region" '"ru-central-1") '('"aws_secret_access_key_secret_name" '"secret_key_name") '('"location" '"protocol://host:port/") '('"source_type" '"PostgreSQL"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithToken) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="YT",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="TOKEN",
- TOKEN_SECRET_NAME="token_name"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"TOKEN") '('"location" '"protocol://host:port/") '('"source_type" '"YT") '('"token_secret_name" '"token_name"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithTablePrefix) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- pragma TablePathPrefix='/aba';
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="NONE"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, "/aba/MyDataSource");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceIfNotExists) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE IF NOT EXISTS MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="NONE"
- );
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"NONE") '('"location" '"my-bucket") '('"source_type" '"ObjectStorage"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObjectIfNotExists"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AlterExternalDataSource) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER EXTERNAL DATA SOURCE MyDataSource
- SET (SOURCE_TYPE = "ObjectStorage", Login = "Admin"),
- SET Location "bucket",
- RESET (Auth_Method, Service_Account_Id, Service_Account_Secret_Name);
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alterObject))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('features '('('"location" '"bucket") '('"login" '"Admin") '('"source_type" '"ObjectStorage"))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('resetFeatures '('"auth_method" '"service_account_id" '"service_account_secret_name")))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceOrReplace) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- CREATE OR REPLACE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="NONE"
- );
- )");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"NONE") '('"location" '"my-bucket") '('"source_type" '"ObjectStorage"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObjectOrReplace"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateOrReplaceForUnsupportedTableTypesShouldFail) {
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE OR REPLACE TABLE t (a int32 not null, primary key(a, a));
- )sql" , "<main>:3:23: Error: OR REPLACE feature is supported only for EXTERNAL DATA SOURCE and EXTERNAL TABLE\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE OR REPLACE TABLE t (
- Key Uint64,
- Value1 String,
- PRIMARY KEY (Key)
- )
- WITH (
- STORE = COLUMN,
- AUTO_PARTITIONING_MIN_PARTITIONS_COUNT = 10
- );
- )sql" , "<main>:3:23: Error: OR REPLACE feature is supported only for EXTERNAL DATA SOURCE and EXTERNAL TABLE\n");
- }
-
- Y_UNIT_TEST(CreateExternalDataSourceWithBadArguments) {
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource;
- )sql" , "<main>:3:56: Error: mismatched input ';' expecting WITH\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- LOCATION="my-bucket",
- AUTH_METHOD="NONE"
- );
- )sql" , "<main>:5:33: Error: SOURCE_TYPE requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket"
- );
- )sql" , "<main>:5:30: Error: AUTH_METHOD requires key\n");
-
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="NONE1"
- );
- )sql" , "<main>:6:33: Error: Unknown AUTH_METHOD = NONE1\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="SERVICE_ACCOUNT"
- );
- )sql" , "<main>:6:33: Error: SERVICE_ACCOUNT_ID requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="SERVICE_ACCOUNT",
- SERVICE_ACCOUNT_ID="s1"
- );
- )sql" , "<main>:7:40: Error: SERVICE_ACCOUNT_SECRET_NAME requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="ObjectStorage",
- LOCATION="my-bucket",
- AUTH_METHOD="SERVICE_ACCOUNT",
- SERVICE_ACCOUNT_SECRET_NAME="s1"
- );
- )sql" , "<main>:7:49: Error: SERVICE_ACCOUNT_ID requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="BASIC",
- LOGIN="admin"
- );
- )sql" , "<main>:7:27: Error: PASSWORD_SECRET_NAME requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="BASIC",
- PASSWORD_SECRET_NAME="secret_name"
- );
- )sql" , "<main>:7:42: Error: LOGIN requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="MDB_BASIC",
- SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name",
- LOGIN="admin",
- PASSWORD_SECRET_NAME="secret_name"
- );
- )sql" , "<main>:9:42: Error: SERVICE_ACCOUNT_ID requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="MDB_BASIC",
- SERVICE_ACCOUNT_ID="sa",
- LOGIN="admin",
- PASSWORD_SECRET_NAME="secret_name"
- );
- )sql" , "<main>:9:42: Error: SERVICE_ACCOUNT_SECRET_NAME requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="MDB_BASIC",
- SERVICE_ACCOUNT_ID="sa",
- SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name",
- PASSWORD_SECRET_NAME="secret_name"
- );
- )sql" , "<main>:9:42: Error: LOGIN requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="MDB_BASIC",
- SERVICE_ACCOUNT_ID="sa",
- SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name",
- LOGIN="admin"
- );
- )sql" , "<main>:9:27: Error: PASSWORD_SECRET_NAME requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="AWS",
- AWS_SECRET_ACCESS_KEY_SECRET_NAME="secret_key_name",
- AWS_REGION="ru-central-1"
- );
- )sql" , "<main>:8:32: Error: AWS_ACCESS_KEY_ID_SECRET_NAME requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="AWS",
- AWS_ACCESS_KEY_ID_SECRET_NAME="secred_id_name",
- AWS_REGION="ru-central-1"
- );
- )sql" , "<main>:8:32: Error: AWS_SECRET_ACCESS_KEY_SECRET_NAME requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
- SOURCE_TYPE="PostgreSQL",
- LOCATION="protocol://host:port/",
- AUTH_METHOD="AWS",
- AWS_SECRET_ACCESS_KEY_SECRET_NAME="secret_key_name",
- AWS_ACCESS_KEY_ID_SECRET_NAME="secred_id_name"
- );
- )sql" , "<main>:8:51: Error: AWS_REGION requires key\n");
- }
-
- Y_UNIT_TEST(DropExternalDataSourceWithTablePrefix) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP EXTERNAL DATA SOURCE MyDataSource;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropExternalDataSource) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- pragma TablePathPrefix='/aba';
- DROP EXTERNAL DATA SOURCE MyDataSource;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, "/aba/MyDataSource");
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropExternalDataSourceIfExists) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP EXTERNAL DATA SOURCE IF EXISTS MyDataSource;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, "MyDataSource");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObjectIfExists"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(ExternalTable) {
- Y_UNIT_TEST(CreateExternalTable) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable (
- a int
- ) WITH (
- DATA_SOURCE="/Root/mydatasource",
- LOCATION="/folder1/*"
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('data_source_path (String '"/Root/mydatasource")) '('location (String '"/folder1/*")))) '('tableType 'externalTable)))))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tablescheme"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalTableWithTablePrefix) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- pragma TablePathPrefix='/aba';
- CREATE EXTERNAL TABLE mytable (
- a int
- ) WITH (
- DATA_SOURCE="mydatasource",
- LOCATION="/folder1/*"
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, "/aba/mydatasource");
- UNIT_ASSERT_STRING_CONTAINS(line, "/aba/mytable");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tablescheme"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalTableObjectStorage) {
- auto res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable (
- a int,
- year Int
- ) WITH (
- DATA_SOURCE="/Root/mydatasource",
- LOCATION="/folder1/*",
- FORMAT="json_as_string",
- `projection.enabled`="true",
- `projection.year.type`="integer",
- `projection.year.min`="2010",
- `projection.year.max`="2022",
- `projection.year.interval`="1",
- `projection.month.type`="integer",
- `projection.month.min`="1",
- `projection.month.max`="12",
- `projection.month.interval`="1",
- `projection.month.digits`="2",
- `storage.location.template`="${year}/${month}",
- PARTITONED_BY = "[year, month]"
- );
- )sql");
- UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString());
- }
-
- Y_UNIT_TEST(CreateExternalTableIfNotExists) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE IF NOT EXISTS mytable (
- a int
- ) WITH (
- DATA_SOURCE="/Root/mydatasource",
- LOCATION="/folder1/*"
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('data_source_path (String '"/Root/mydatasource")) '('location (String '"/folder1/*")))) '('tableType 'externalTable)))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, "create_if_not_exists");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalTableOrReplace) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- CREATE OR REPLACE EXTERNAL TABLE mytable (
- a int
- ) WITH (
- DATA_SOURCE="/Root/mydatasource",
- LOCATION="/folder1/*"
- );
- )");
- UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('data_source_path (String '"/Root/mydatasource")) '('location (String '"/folder1/*")))) '('tableType 'externalTable)))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, "create_or_replace");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AlterExternalTableAddColumn) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER EXTERNAL TABLE mytable
- ADD COLUMN my_column int32,
- RESET (LOCATION);
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('actions '('('addColumns '('('"my_column" (AsOptionalType (DataType 'Int32))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('setTableSettings '('('location)))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('tableType 'externalTable))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alter))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AlterExternalTableDropColumn) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER EXTERNAL TABLE mytable
- DROP COLUMN my_column,
- SET (Location = "abc", Other_Prop = "42"),
- SET x 'y';
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('actions '('('dropColumns '('"my_column")#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('setTableSettings '('('location (String '"abc")) '('Other_Prop (String '"42")) '('x (String '"y")))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('tableType 'externalTable))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alter))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateExternalTableWithBadArguments) {
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable;
- )sql" , "<main>:3:45: Error: mismatched input ';' expecting '('\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable (
- a int
- );
- )sql" , "<main>:4:23: Error: DATA_SOURCE requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable (
- a int
- ) WITH (
- DATA_SOURCE="/Root/mydatasource"
- );
- )sql" , "<main>:6:33: Error: LOCATION requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable (
- a int
- ) WITH (
- LOCATION="/folder1/*"
- );
- )sql" , "<main>:6:30: Error: DATA_SOURCE requires key\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE EXTERNAL TABLE mytable (
- a int,
- PRIMARY KEY(a)
- ) WITH (
- DATA_SOURCE="/Root/mydatasource",
- LOCATION="/folder1/*"
- );
- )sql" , "<main>:8:30: Error: PRIMARY KEY is not supported for external table\n");
- }
-
- Y_UNIT_TEST(DropExternalTable) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP EXTERNAL TABLE MyExternalTable;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("tablescheme"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropExternalTableWithTablePrefix) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- pragma TablePathPrefix='/aba';
- DROP EXTERNAL TABLE MyExternalTable;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, "/aba/MyExternalTable");
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'tablescheme"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropExternalTableIfExists) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP EXTERNAL TABLE IF EXISTS MyExternalTable;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("tablescheme"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop_if_exists"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(TopicsDDL) {
- void TestQuery(const TString& query, bool expectOk = true) {
- TStringBuilder finalQuery;
-
- finalQuery << "use plato;" << Endl << query;
- auto res = SqlToYql(finalQuery, 10, "kikimr");
- if (expectOk) {
- UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString());
- } else {
- UNIT_ASSERT(!res.IsOk());
- }
- }
-
- Y_UNIT_TEST(CreateTopicSimple) {
- TestQuery(R"(
- CREATE TOPIC topic1;
- )");
- TestQuery(R"(
- CREATE TOPIC `cluster1.topic1`;
- )");
- TestQuery(R"(
- CREATE TOPIC topic1 WITH (metering_mode = "str_value", partition_count_limit = 123, retention_period = Interval('PT1H'));
- )");
- }
-
- Y_UNIT_TEST(CreateTopicConsumer) {
- TestQuery(R"(
- CREATE TOPIC topic1 (CONSUMER cons1);
- )");
- TestQuery(R"(
- CREATE TOPIC topic1 (CONSUMER cons1, CONSUMER cons2 WITH (important = false));
- )");
- TestQuery(R"(
- CREATE TOPIC topic1 (CONSUMER cons1, CONSUMER cons2 WITH (important = false)) WITH (supported_codecs = "1,2,3");
- )");
- }
-
- Y_UNIT_TEST(AlterTopicSimple) {
- TestQuery(R"(
- ALTER TOPIC topic1 SET (retention_period = Interval('PT1H'));
- )");
- TestQuery(R"(
- ALTER TOPIC topic1 SET (retention_storage_mb = 3, partition_count_limit = 50);
- )");
- TestQuery(R"(
- ALTER TOPIC topic1 RESET (supported_codecs, retention_period);
- )");
- TestQuery(R"(
- ALTER TOPIC topic1 RESET (partition_write_speed_bytes_per_second),
- SET (partition_write_burst_bytes = 11111, min_active_partitions = 1);
- )");
- }
- Y_UNIT_TEST(AlterTopicConsumer) {
- TestQuery(R"(
- ALTER TOPIC topic1 ADD CONSUMER consumer1,
- ADD CONSUMER consumer2 WITH (important = false, supported_codecs = "RAW"),
- ALTER CONSUMER consumer3 SET (important = false, read_from = 1),
- ALTER CONSUMER consumer3 RESET (supported_codecs),
- DROP CONSUMER consumer4,
- SET (partition_count_limit = 11, retention_period = Interval('PT1H')),
- RESET(metering_mode)
- )");
- }
- Y_UNIT_TEST(DropTopic) {
- TestQuery(R"(
- DROP TOPIC topic1;
- )");
- }
-
- Y_UNIT_TEST(TopicBadRequests) {
- TestQuery(R"(
- CREATE TOPIC topic1();
- )", false);
- TestQuery(R"(
- CREATE TOPIC topic1 SET setting1 = value1;
- )", false);
- TestQuery(R"(
- ALTER TOPIC topic1 SET setting1 value1;
- )", false);
- TestQuery(R"(
- ALTER TOPIC topic1 RESET setting1;
- )", false);
-
- TestQuery(R"(
- ALTER TOPIC topic1 DROP CONSUMER consumer4 WITH (k1 = v1);
- )", false);
-
- TestQuery(R"(
- CREATE TOPIC topic1 WITH (retention_period = 123);
- )", false);
- TestQuery(R"(
- CREATE TOPIC topic1 (CONSUMER cons1, CONSUMER cons1 WITH (important = false));
- )", false);
- TestQuery(R"(
- CREATE TOPIC topic1 (CONSUMER cons1 WITH (bad_option = false));
- )", false);
- TestQuery(R"(
- ALTER TOPIC topic1 ADD CONSUMER cons1, ALTER CONSUMER cons1 RESET (important);
- )", false);
- TestQuery(R"(
- ALTER TOPIC topic1 ADD CONSUMER consumer1,
- ALTER CONSUMER consumer3 SET (supported_codecs = "RAW", read_from = 1),
- ALTER CONSUMER consumer3 RESET (supported_codecs);
- )", false);
- TestQuery(R"(
- ALTER TOPIC topic1 ADD CONSUMER consumer1,
- ALTER CONSUMER consumer3 SET (supported_codecs = "RAW", read_from = 1),
- ALTER CONSUMER consumer3 SET (read_from = 2);
- )", false);
- }
-
- Y_UNIT_TEST(TopicWithPrefix) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- PRAGMA TablePathPrefix = '/database/path/to/tables';
- ALTER TOPIC `my_table/my_feed` ADD CONSUMER `my_consumer`;
- )");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("/database/path/to/tables/my_table/my_feed"), 0}, {"topic", 0}};
- VerifyProgram(res, elementStat);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["topic"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["/database/path/to/tables/my_table/my_feed"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(BlockEnginePragma) {
- Y_UNIT_TEST(Basic) {
- const TVector<TString> values = {"auto", "force", "disable"};
- for (const auto& value : values) {
- const auto query = TStringBuilder() << "pragma Blockengine='" << value << "'; select 1;";
- NYql::TAstParseResult res = SqlToYql(query);
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- Y_UNUSED(word);
- UNIT_ASSERT_STRING_CONTAINS(line, TStringBuilder() << R"(Configure! world (DataSource '"config") '"BlockEngine" '")" << value << "\"");
- };
-
- TWordCountHive elementStat({"BlockEngine"});
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT(elementStat["BlockEngine"] == ((value == "disable") ? 0 : 1));
- }
- }
-
- Y_UNIT_TEST(UnknownSetting) {
- ExpectFailWithError("use plato; pragma BlockEngine='foo';",
- "<main>:1:31: Error: Expected `disable|auto|force' argument for: BlockEngine\n");
- }
-}
-
-Y_UNIT_TEST_SUITE(TViewSyntaxTest) {
- Y_UNIT_TEST(CreateViewSimple) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- CREATE VIEW TheView WITH (security_invoker = TRUE) AS SELECT 1;
- )"
- );
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
- }
-
- Y_UNIT_TEST(CreateViewIfNotExists) {
- constexpr const char* name = "TheView";
- NYql::TAstParseResult res = SqlToYql(std::format(R"(
- USE plato;
- CREATE VIEW IF NOT EXISTS {} AS SELECT 1;
- )", name
- ));
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_STRING_CONTAINS(line, name);
- UNIT_ASSERT_STRING_CONTAINS(line, "createObjectIfNotExists");
- }
- };
-
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(CreateViewFromTable) {
- constexpr const char* path = "/PathPrefix/TheView";
- constexpr const char* query = R"(
- SELECT * FROM SomeTable
- )";
-
- NYql::TAstParseResult res = SqlToYql(std::format(R"(
- USE plato;
- CREATE VIEW `{}` WITH (security_invoker = TRUE) AS {};
- )",
- path,
- query
- )
- );
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_STRING_CONTAINS(line, path);
- UNIT_ASSERT_STRING_CONTAINS(line, "createObject");
- }
- };
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(CheckReconstructedQuery) {
- constexpr const char* path = "/PathPrefix/TheView";
- constexpr const char* query = R"(
- SELECT * FROM FirstTable JOIN SecondTable ON FirstTable.key == SecondTable.key
- )";
-
- NYql::TAstParseResult res = SqlToYql(std::format(R"(
- USE plato;
- CREATE VIEW `{}` WITH (security_invoker = TRUE) AS {};
- )",
- path,
- query
- )
- );
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TString reconstructedQuery = ToString(Tokenize(query));
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- if (word == "query_text") {
- UNIT_ASSERT_STRING_CONTAINS(line, reconstructedQuery);
- }
- };
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(DropView) {
- constexpr const char* path = "/PathPrefix/TheView";
- NYql::TAstParseResult res = SqlToYql(std::format(R"(
- USE plato;
- DROP VIEW `{}`;
- )",
- path
- )
- );
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_STRING_CONTAINS(line, path);
- UNIT_ASSERT_STRING_CONTAINS(line, "dropObject");
- }
- };
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(DropViewIfExists) {
- constexpr const char* name = "TheView";
- NYql::TAstParseResult res = SqlToYql(std::format(R"(
- USE plato;
- DROP VIEW IF EXISTS {};
- )", name
- ));
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_STRING_CONTAINS(line, name);
- UNIT_ASSERT_STRING_CONTAINS(line, "dropObjectIfExists");
- }
- };
-
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(CreateViewWithTablePrefix) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- PRAGMA TablePathPrefix='/PathPrefix';
- CREATE VIEW TheView WITH (security_invoker = TRUE) AS SELECT 1;
- )"
- );
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write!") {
- UNIT_ASSERT_STRING_CONTAINS(line, "/PathPrefix/TheView");
- UNIT_ASSERT_STRING_CONTAINS(line, "createObject");
- }
- };
-
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(DropViewWithTablePrefix) {
- NYql::TAstParseResult res = SqlToYql(R"(
- USE plato;
- PRAGMA TablePathPrefix='/PathPrefix';
- DROP VIEW TheView;
- )"
- );
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, "/PathPrefix/TheView");
- UNIT_ASSERT_STRING_CONTAINS(line, "dropObject");
- }
- };
-
- TWordCountHive elementStat = { {"Write!"} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
- }
-
- Y_UNIT_TEST(YtAlternativeSchemaSyntax) {
- NYql::TAstParseResult res = SqlToYql(R"(
- SELECT * FROM plato.Input WITH schema(y Int32, x String not null);
- )");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "userschema") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
- line.find(R"__('('('"userschema" (StructType '('"y" (AsOptionalType (DataType 'Int32))) '('"x" (DataType 'String))))))__"));
- }
- };
-
- TWordCountHive elementStat = {{TString("userschema"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["userschema"]);
- }
-
- Y_UNIT_TEST(UseViewAndFullColumnId) {
- NYql::TAstParseResult res = SqlToYql("USE plato; SELECT Input.x FROM Input VIEW uitzicht;");
- UNIT_ASSERT(res.Root);
-
- TWordCountHive elementStat = {{TString("SqlAccess"), 0}, {"SqlProjectItem", 0}, {"Read!", 0}};
- VerifyProgram(res, elementStat);
- UNIT_ASSERT_VALUES_EQUAL(0, elementStat["SqlAccess"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Read!"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(CompactNamedExprs) {
- Y_UNIT_TEST(SourceCallablesInWrongContext) {
- TString query = R"(
- pragma CompactNamedExprs;
- $foo = %s();
- select $foo from plato.Input;
- )";
-
- THashMap<TString, TString> errs = {
- {"TableRow", "<main>:3:20: Error: TableRow requires data source\n"},
- {"JoinTableRow", "<main>:3:20: Error: JoinTableRow requires data source\n"},
- {"TableRecordIndex", "<main>:3:20: Error: Unable to use function: TableRecord without source\n"},
- {"TablePath", "<main>:3:20: Error: Unable to use function: TablePath without source\n"},
- {"SystemMetadata", "<main>:3:20: Error: Unable to use function: SystemMetadata without source\n"},
- };
-
- for (TString callable : { "TableRow", "JoinTableRow", "TableRecordIndex", "TablePath", "SystemMetadata"}) {
- auto req = Sprintf(query.c_str(), callable.c_str());
- ExpectFailWithError(req, errs[callable]);
- }
- }
-
- Y_UNIT_TEST(ValidateUnusedExprs) {
- TString query = R"(
- pragma warning("disable", "4527");
- pragma CompactNamedExprs;
- pragma ValidateUnusedExprs;
-
- $foo = count(1);
- select 1;
- )";
- ExpectFailWithError(query, "<main>:6:20: Error: Aggregation is not allowed in this context\n");
- query = R"(
- pragma warning("disable", "4527");
- pragma CompactNamedExprs;
- pragma ValidateUnusedExprs;
-
- define subquery $x() as
- select count(1, 2);
- end define;
- select 1;
- )";
- ExpectFailWithError(query, "<main>:7:24: Error: Aggregation function Count requires exactly 1 argument(s), given: 2\n");
- }
-
- Y_UNIT_TEST(DisableValidateUnusedExprs) {
- TString query = R"(
- pragma warning("disable", "4527");
- pragma CompactNamedExprs;
- pragma DisableValidateUnusedExprs;
-
- $foo = count(1);
- select 1;
- )";
- SqlToYql(query).IsOk();
- query = R"(
- pragma warning("disable", "4527");
- pragma CompactNamedExprs;
- pragma DisableValidateUnusedExprs;
-
- define subquery $x() as
- select count(1, 2);
- end define;
- select 1;
- )";
- SqlToYql(query).IsOk();
- }
-}
-
-Y_UNIT_TEST_SUITE(ResourcePool) {
- Y_UNIT_TEST(CreateResourcePool) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE RESOURCE POOL MyResourcePool WITH (
- CONCURRENT_QUERY_LIMIT=20,
- QUERY_CANCEL_AFTER_SECONDS=86400,
- QUEUE_TYPE="FIFO"
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"concurrent_query_limit" (Int32 '"20")) '('"query_cancel_after_seconds" (Int32 '"86400")) '('"queue_type" '"FIFO"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateResourcePoolWithBadArguments) {
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE RESOURCE POOL MyResourcePool;
- )sql" , "<main>:3:51: Error: mismatched input ';' expecting WITH\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE RESOURCE POOL MyResourcePool WITH (
- DUPLICATE_SETTING="first_value",
- DUPLICATE_SETTING="second_value"
- );
- )sql" , "<main>:5:21: Error: DUPLICATE_SETTING duplicate keys\n");
- }
-
- Y_UNIT_TEST(AlterResourcePool) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER RESOURCE POOL MyResourcePool
- SET (CONCURRENT_QUERY_LIMIT = 30, Weight = 5, QUEUE_TYPE = "UNORDERED"),
- RESET (Query_Cancel_After_Seconds, Query_Count_Limit);
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alterObject))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('features '('('"concurrent_query_limit" (Int32 '"30")) '('"queue_type" '"UNORDERED") '('"weight" (Int32 '"5")))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('resetFeatures '('"query_cancel_after_seconds" '"query_count_limit")))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropResourcePool) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP RESOURCE POOL MyResourcePool;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(BackupCollection) {
- Y_UNIT_TEST(CreateBackupCollection) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TestCollection WITH (
- STORAGE="local",
- TAG="test" -- for testing purposes, not a real thing
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"storage" (String '"local")))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"tag" (String '"test"))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('entries '()))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'create"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateBackupCollectionWithDatabase) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TestCollection DATABASE WITH (
- STORAGE="local",
- TAG="test" -- for testing purposes, not a real thing
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"storage" (String '"local")))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"tag" (String '"test"))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('entries '('('('type 'database)))))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'create"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateBackupCollectionWithTables) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TestCollection (
- TABLE someTable,
- TABLE `prefix/anotherTable`
- ) WITH (
- STORAGE="local",
- TAG="test" -- for testing purposes, not a real thing
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"storage" (String '"local")))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"tag" (String '"test"))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('entries '('('('type 'table) '('path '"someTable")) '('('type 'table) '('path '"prefix/anotherTable")))))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'create"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateBackupCollectionWithBadArguments) {
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TestCollection;
- )sql" , "<main>:3:55: Error: mismatched input ';' expecting {'(', DATABASE, WITH}\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TABLE TestCollection;
- )sql" , "<main>:3:47: Error: mismatched input 'TestCollection' expecting {'(', DATABASE, WITH}\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION DATABASE `test` TestCollection;
- )sql" , "<main>:3:50: Error: mismatched input '`test`' expecting {'(', DATABASE, WITH}\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TestCollection WITH (
- DUPLICATE_SETTING="first_value",
- DUPLICATE_SETTING="second_value"
- );
- )sql" , "<main>:5:21: Error: DUPLICATE_SETTING duplicate keys\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE BACKUP COLLECTION TestCollection WITH (
- INT_SETTING=1
- );
- )sql" , "<main>:4:21: Error: INT_SETTING value should be a string literal\n");
- }
-
- Y_UNIT_TEST(AlterBackupCollection) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER BACKUP COLLECTION TestCollection
- SET (STORAGE="remote"), -- also just for test
- SET (TAG1 = "123"),
- RESET (TAG2, TAG3);
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alter))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"storage" (String '"remote")))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"tag1" (String '"123"))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('resetSettings '('"tag2" '"tag3")))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AlterBackupCollectionEntries) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER BACKUP COLLECTION TestCollection
- DROP TABLE `test`,
- ADD DATABASE;
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alter))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('alterEntries)#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('type 'table) '('path '"test") '('action 'drop)))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('type 'database) '('action 'add)))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropBackupCollection) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP BACKUP COLLECTION TestCollection;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(ResourcePoolClassifier) {
- Y_UNIT_TEST(CreateResourcePoolClassifier) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- CREATE RESOURCE POOL CLASSIFIER MyResourcePoolClassifier WITH (
- RANK=20,
- RESOURCE_POOL='wgUserQueries',
- MEMBER_NAME='yandex_query@abc'
- );
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"member_name" '"yandex_query@abc") '('"rank" (Int32 '"20")) '('"resource_pool" '"wgUserQueries"))#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(CreateResourcePoolClassifierWithBadArguments) {
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE RESOURCE POOL CLASSIFIER MyResourcePoolClassifier;
- )sql" , "<main>:3:72: Error: mismatched input ';' expecting WITH\n");
-
- ExpectFailWithError(R"sql(
- USE plato;
- CREATE RESOURCE POOL CLASSIFIER MyResourcePoolClassifier WITH (
- DUPLICATE_SETTING="first_value",
- DUPLICATE_SETTING="second_value"
- );
- )sql" , "<main>:5:21: Error: DUPLICATE_SETTING duplicate keys\n");
- }
-
- Y_UNIT_TEST(AlterResourcePoolClassifier) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- ALTER RESOURCE POOL CLASSIFIER MyResourcePoolClassifier
- SET (RANK = 30, Weight = 5, MEMBER_NAME = "test@user"),
- RESET (Resource_Pool);
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alterObject))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('features '('('"member_name" '"test@user") '('"rank" (Int32 '"30")) '('"weight" (Int32 '"5")))))#");
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('('resetFeatures '('"resource_pool")))#");
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(DropResourcePoolClassifier) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- DROP RESOURCE POOL CLASSIFIER MyResourcePoolClassifier;
- )sql");
- UNIT_ASSERT(res.Root);
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0}};
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(BacktickMatching) {
- auto req = "select\n"
- " 1 as `Schema has \\`RealCost\\``\n"
- " -- foo`bar";
- auto res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
-
- req = "select 1 as `a``b`, 2 as ````, 3 as `\\x60a\\x60`, 4 as ```b```, 5 as `\\`c\\``";
- res = SqlToYql(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- res = SqlToYqlWithAnsiLexer(req);
- UNIT_ASSERT(res.Root);
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-}
-
-Y_UNIT_TEST_SUITE(Backup) {
- Y_UNIT_TEST(Simple) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- BACKUP TestCollection;
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'Incremental"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'backup"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(Incremental) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- BACKUP TestCollection INCREMENTAL;
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'backupIncremental"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(Restore) {
- Y_UNIT_TEST(Simple) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- RESTORE TestCollection;
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'restore"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-
- Y_UNIT_TEST(AtPoint) {
- NYql::TAstParseResult res = SqlToYql(R"sql(
- USE plato;
- RESTORE TestCollection AT '2024-06-16_20-14-02';
- )sql");
- UNIT_ASSERT_C(res.Root, res.Issues.ToString());
-
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
- UNIT_ASSERT_STRING_CONTAINS(line, R"#('at '"2024-06-16_20-14-02")#");
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'restore"));
- }
- };
-
- TWordCountHive elementStat = { {TString("Write"), 0} };
- VerifyProgram(res, elementStat, verifyLine);
-
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- }
-}
-
-Y_UNIT_TEST_SUITE(ColumnFamily) {
- Y_UNIT_TEST(CompressionLevelCorrectUsage) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- CREATE TABLE tableName (
- Key Uint32 FAMILY default,
- Value String FAMILY family1,
- PRIMARY KEY (Key),
- FAMILY default (
- DATA = "test",
- COMPRESSION = "lz4",
- COMPRESSION_LEVEL = 5
- ),
- FAMILY family1 (
- DATA = "test",
- COMPRESSION = "lz4",
- COMPRESSION_LEVEL = 3
- )
- );
- )");
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
- if (word == "Write") {
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("compression_level"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("5"));
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("3"));
- }
- };
-
- TWordCountHive elementStat = { { TString("Write"), 0 }, { TString("compression_level"), 0 } };
- VerifyProgram(res, elementStat, verifyLine);
- UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
- UNIT_ASSERT_VALUES_EQUAL(2, elementStat["compression_level"]);
- }
-
- Y_UNIT_TEST(FieldDataIsNotString) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- CREATE TABLE tableName (
- Key Uint32 FAMILY default,
- PRIMARY KEY (Key),
- FAMILY default (
- DATA = 1,
- COMPRESSION = "lz4",
- COMPRESSION_LEVEL = 5
- )
- );
- )");
- UNIT_ASSERT(!res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 1);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "DATA value should be a string literal");
- }
-
- Y_UNIT_TEST(FieldCompressionIsNotString) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- CREATE TABLE tableName (
- Key Uint32 FAMILY default,
- PRIMARY KEY (Key),
- FAMILY default (
- DATA = "test",
- COMPRESSION = 2,
- COMPRESSION_LEVEL = 5
- ),
- );
- )");
- UNIT_ASSERT(!res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 1);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "COMPRESSION value should be a string literal");
- }
-
- Y_UNIT_TEST(FieldCompressionLevelIsNotInteger) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- CREATE TABLE tableName (
- Key Uint32 FAMILY default,
- PRIMARY KEY (Key),
- FAMILY default (
- DATA = "test",
- COMPRESSION = "lz4",
- COMPRESSION_LEVEL = "5"
- )
- );
- )");
- UNIT_ASSERT(!res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 1);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "COMPRESSION_LEVEL value should be an integer");
- }
-
- Y_UNIT_TEST(AlterCompressionCorrectUsage) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- ALTER TABLE tableName ALTER FAMILY default SET COMPRESSION "lz4";
- )");
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(AlterCompressionFieldIsNotString) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- ALTER TABLE tableName ALTER FAMILY default SET COMPRESSION lz4;
- )");
- UNIT_ASSERT(!res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 1);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "mismatched input 'lz4' expecting {STRING_VALUE, DIGITS, INTEGER_VALUE}");
- }
-
- Y_UNIT_TEST(AlterCompressionLevelCorrectUsage) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- ALTER TABLE tableName ALTER FAMILY default SET COMPRESSION_LEVEL 5;
- )");
- UNIT_ASSERT(res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 0);
- }
-
- Y_UNIT_TEST(AlterCompressionLevelFieldIsNotInteger) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- ALTER TABLE tableName ALTER FAMILY default SET COMPRESSION_LEVEL "5";
- )");
- UNIT_ASSERT(!res.IsOk());
- UNIT_ASSERT(res.Issues.Size() == 1);
- UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "COMPRESSION_LEVEL value should be an integer");
- }
-}
+#define ANTLR_VER 4
+#include "sql_ut_common.h"
Y_UNIT_TEST_SUITE(QuerySplit) {
Y_UNIT_TEST(Simple) {
@@ -8180,86 +97,4 @@ Y_UNIT_TEST_SUITE(QuerySplit) {
}
}
-Y_UNIT_TEST_SUITE(Transfer) {
- Y_UNIT_TEST(Lambda) {
- NYql::TAstParseResult res = SqlToYql(R"( use plato;
- -- Русский коммент, empty statement
- ;
-
- -- befor comment
- $a = "А";
-
- SELECT * FROM Input;
-
- $b = ($x) -> { return $a || $x; };
-
- CREATE TRANSFER `TransferName`
- FROM `TopicName` TO `TableName`
- USING ($x) -> {
- -- internal comment
- return $b($x);
- }
- WITH (
- CONNECTION_STRING = "grpc://localhost:2135/?database=/Root"
- );
- )");
-
- UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString());
- UNIT_ASSERT_VALUES_EQUAL_C(res.Issues.Size(), 0, res.Issues.ToString());
-
- const auto programm = GetPrettyPrint(res);
-
- Cerr << ">>>>> Root " << programm << Endl;
- auto expected = R"('transformLambda 'use plato;
--- befor comment
- $a = "А";
-$b = ($x) -> { return $a || $x; };
-$__ydb_transfer_lambda = ($x) -> {
- -- internal comment
- return $b($x);
- };
-))";
-
- UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, programm.find(expected));
-
- }
-}
-
-Y_UNIT_TEST_SUITE(MatchRecognizeMeasuresAggregation) {
- Y_UNIT_TEST(InsideSelect) {
- ExpectFailWithError(R"sql(
- SELECT FIRST(0), LAST(1);
- )sql",
- "<main>:2:20: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- "<main>:2:30: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- );
- }
-
- Y_UNIT_TEST(OutsideSelect) {
- ExpectFailWithError(R"sql(
- $lambda = ($x) -> (FIRST($x) + LAST($x));
- SELECT $lambda(x) FROM plato.Input;
- )sql",
- "<main>:2:32: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- "<main>:2:44: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- );
- }
-
- Y_UNIT_TEST(AsAggregateFunction) {
- ExpectFailWithError(R"sql(
- SELECT FIRST(x), LAST(x) FROM plato.Input;
- )sql",
- "<main>:2:20: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- "<main>:2:30: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- );
- }
- Y_UNIT_TEST(AsWindowFunction) {
- ExpectFailWithError(R"sql(
- SELECT FIRST(x) OVER(), LAST(x) OVER() FROM plato.Input;
- )sql",
- "<main>:2:20: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- "<main>:2:37: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
- );
- }
-}
diff --git a/yql/essentials/sql/v1/sql_ut_antlr4.h b/yql/essentials/sql/v1/sql_ut_antlr4.h
index 4f4203a789..03d804707f 100644
--- a/yql/essentials/sql/v1/sql_ut_antlr4.h
+++ b/yql/essentials/sql/v1/sql_ut_antlr4.h
@@ -10,6 +10,7 @@
#include <library/cpp/testing/unittest/registar.h>
+#include <library/cpp/regex/pcre/pcre.h>
#include <util/string/split.h>
#include <deque>
#include <unordered_set>
@@ -84,6 +85,13 @@ inline void ExpectFailWithError(const TString& query, const TString& error) {
UNIT_ASSERT_NO_DIFF(Err2Str(res), error);
}
+inline void ExpectFailWithFuzzyError(const TString& query, const TString& errorRegex) {
+ NYql::TAstParseResult res = SqlToYql(query);
+
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT(NPcre::TPcre<char>(errorRegex.c_str()).Matches(Err2Str(res)));
+}
+
inline NYql::TAstParseResult SqlToYqlWithAnsiLexer(const TString& query, size_t maxErrors = 10, const TString& provider = {}, EDebugOutput debug = EDebugOutput::None) {
bool ansiLexer = true;
return SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, maxErrors, provider, debug, ansiLexer);
diff --git a/yql/essentials/sql/v1/sql_ut_common.h b/yql/essentials/sql/v1/sql_ut_common.h
new file mode 100644
index 0000000000..325b439b25
--- /dev/null
+++ b/yql/essentials/sql/v1/sql_ut_common.h
@@ -0,0 +1,8398 @@
+namespace {
+ TString ToString(const TParsedTokenList& tokens) {
+ TStringBuilder reconstructedQuery;
+ for (const auto& token : tokens) {
+ if (token.Name == "WS" || token.Name == "EOF") {
+ continue;
+ }
+ if (!reconstructedQuery.empty()) {
+ reconstructedQuery << ' ';
+ }
+ reconstructedQuery << token.Content;
+ }
+ return reconstructedQuery;
+ }
+}
+
+Y_UNIT_TEST_SUITE(AnsiMode) {
+ Y_UNIT_TEST(PragmaAnsi) {
+ UNIT_ASSERT(SqlToYql("PRAGMA ANSI 2016;").IsOk());
+ }
+}
+
+Y_UNIT_TEST_SUITE(SqlParsingOnly) {
+ ///This function is used in BACKWARD COMPATIBILITY tests below that LIMIT the sets of token that CAN NOT be used
+ ///as identifiers in different contexts in a SQL request
+ ///\return list of tokens that failed this check
+ TVector<TString> ValidateTokens(const THashSet<TString>& forbidden, const std::function<TString (const TString& )>& makeRequest) {
+ THashMap<TString, bool> allTokens;
+ for (const auto& t: NSQLFormat::GetKeywords()) {
+ allTokens[t] = !forbidden.contains((t));
+ }
+ for (const auto& f: forbidden) {
+ UNIT_ASSERT(allTokens.contains(f)); //check that forbidden list contains tokens only(argument check)
+ }
+ TVector<TString> failed;
+ for (const auto& [token, allowed]: allTokens) {
+ if (SqlToYql(makeRequest(token)).IsOk() != allowed)
+ failed.push_back(token);
+ }
+ return failed;
+ }
+
+ Y_UNIT_TEST(TokensAsColumnName) { //id_expr
+ auto failed = ValidateTokens({
+ "ALL", "ANY", "AS", "ASSUME", "ASYMMETRIC", "AUTOMAP", "BETWEEN", "BITCAST",
+ "CALLABLE", "CASE", "CAST", "CUBE", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
+ "DICT", "DISTINCT", "ENUM", "ERASE", "EXCEPT", "EXISTS", "FLOW", "FROM", "FULL",
+ "HAVING", "HOP", "INTERSECT", "JSON_EXISTS", "JSON_QUERY", "JSON_VALUE", "LIMIT", "LIST",
+ "NOT", "OPTIONAL", "PROCESS", "REDUCE", "REPEATABLE", "RESOURCE", "RETURN", "RETURNING", "ROLLUP",
+ "SELECT", "SET", "STREAM", "STRUCT", "SYMMETRIC", "TAGGED", "TUPLE", "UNBOUNDED",
+ "UNION", "VARIANT", "WHEN", "WHERE", "WINDOW", "WITHOUT"
+ },
+ [](const TString& token){
+ TStringBuilder req;
+ req << "SELECT " << token << " FROM Plato.Input";
+ return req;
+ }
+ );
+ UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
+ }
+
+ Y_UNIT_TEST(TokensAsWithoutColumnName) { //id_without
+ auto failed = ValidateTokens({
+ "ALL", "AS", "ASSUME", "ASYMMETRIC", "AUTOMAP", "BETWEEN", "BITCAST",
+ "CALLABLE", "CASE", "CAST", "CUBE", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
+ "DICT", "DISTINCT", "EMPTY_ACTION", "ENUM", "EXCEPT", "EXISTS", "FALSE", "FLOW", "FROM", "FULL",
+ "HAVING", "HOP", "INTERSECT", "JSON_EXISTS", "JSON_QUERY", "JSON_VALUE", "LIMIT", "LIST",
+ "NOT", "NULL", "OPTIONAL", "PROCESS", "REDUCE", "REPEATABLE", "RESOURCE", "RETURN", "RETURNING", "ROLLUP",
+ "SELECT", "SET", "STRUCT", "SYMMETRIC", "TAGGED", "TRUE", "TUPLE", "UNBOUNDED",
+ "UNION", "VARIANT", "WHEN", "WHERE", "WINDOW", "WITHOUT"
+ },
+ [](const TString& token){
+ TStringBuilder req;
+ req << "SELECT * WITHOUT " << token << " FROM Plato.Input";
+ return req;
+ }
+ );
+ UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
+ }
+
+ Y_UNIT_TEST(TokensAsColumnNameInAddColumn) { //id_schema
+ auto failed = ValidateTokens({
+ "ANY", "AUTOMAP", "CALLABLE", "COLUMN", "DICT", "ENUM", "ERASE", "FALSE", "FLOW",
+ "LIST", "OPTIONAL", "REPEATABLE", "RESOURCE",
+ "SET", "STREAM", "STRUCT", "TAGGED", "TRUE", "TUPLE", "VARIANT"
+ },
+ [](const TString& token){
+ TStringBuilder req;
+ req << "ALTER TABLE Plato.Input ADD COLUMN " << token << " Bool";
+ return req;
+ }
+ );
+ UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
+ }
+
+ Y_UNIT_TEST(TokensAsColumnAlias) {
+ auto failed = ValidateTokens({
+ "AUTOMAP", "FALSE",
+ "REPEATABLE", "TRUE"
+ },
+ [](const TString& token){
+ TStringBuilder req;
+ req << "SELECT Col as " << token << " FROM Plato.Input";
+ return req;
+ }
+ );
+ UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
+ }
+
+ Y_UNIT_TEST(TokensAsTableName) { //id_table_or_type
+ auto failed = ValidateTokens({
+ "ANY", "AUTOMAP", "COLUMN", "ERASE", "FALSE",
+ "REPEATABLE", "STREAM", "TRUE"
+ },
+ [](const TString& token){
+ TStringBuilder req;
+ req << "SELECT * FROM Plato." << token;
+ return req;
+ }
+ );
+ UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
+ }
+
+ Y_UNIT_TEST(TokensAsTableAlias) { //id_table
+ auto failed = ValidateTokens({
+ "AUTOMAP", "CALLABLE", "DICT", "ENUM","FALSE", "FLOW",
+ "LIST", "OPTIONAL", "REPEATABLE", "RESOURCE",
+ "SET", "STRUCT", "TAGGED", "TRUE", "TUPLE", "VARIANT"
+ },
+ [](const TString& token){
+ TStringBuilder req;
+ req << "SELECT * FROM Plato.Input AS " << token;
+ return req;
+ }
+ );
+ UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
+ }
+
+ Y_UNIT_TEST(TokensAsHints) { //id_hint
+ auto failed = ValidateTokens({
+ "AUTOMAP", "CALLABLE", "COLUMNS", "DICT", "ENUM", "FALSE", "FLOW",
+ "LIST", "OPTIONAL", "REPEATABLE", "RESOURCE",
+ "SCHEMA", "SET", "STRUCT", "TAGGED", "TRUE", "TUPLE", "VARIANT"
+ },
+ [](const TString& token){
+ TStringBuilder req;
+ req << "SELECT * FROM Plato.Input WITH " << token;
+ return req;
+ }
+ );
+ UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
+ }
+
+ Y_UNIT_TEST(TokensAsWindow) { //id_window
+ auto failed = ValidateTokens({
+ "AUTOMAP", "CALLABLE", "DICT", "ENUM", "FALSE", "FLOW", "GROUPS", "LIST", "OPTIONAL",
+ "RANGE", "REPEATABLE", "RESOURCE", "ROWS", "SET", "STRUCT", "TAGGED" ,"TRUE", "TUPLE", "VARIANT"
+ },
+ [](const TString& token){
+ TStringBuilder req;
+ req << "SELECT * FROM Plato.Input WINDOW " << token << " AS ()";
+ return req;
+ }
+ );
+ UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
+ }
+
+ Y_UNIT_TEST(TokensAsIdExprIn) { //id_expr_in
+ auto failed = ValidateTokens({
+ "ALL", "ANY", "AS", "ASSUME", "ASYMMETRIC", "AUTOMAP", "BETWEEN", "BITCAST",
+ "CALLABLE", "CASE", "CAST", "COMPACT", "CUBE", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
+ "DICT", "DISTINCT", "ENUM", "ERASE", "EXCEPT", "EXISTS", "FLOW", "FROM", "FULL",
+ "HAVING", "HOP", "INTERSECT", "JSON_EXISTS", "JSON_QUERY", "JSON_VALUE", "LIMIT", "LIST",
+ "NOT", "OPTIONAL", "PROCESS", "REDUCE", "REPEATABLE", "RESOURCE", "RETURN", "RETURNING", "ROLLUP",
+ "SELECT", "SET", "STREAM", "STRUCT", "SYMMETRIC", "TAGGED", "TUPLE", "UNBOUNDED",
+ "UNION", "VARIANT", "WHEN", "WHERE", "WINDOW", "WITHOUT"
+ },
+ [](const TString& token){
+ TStringBuilder req;
+ req << "SELECT * FROM Plato.Input WHERE q IN " << token;
+ return req;
+ }
+ );
+ UNIT_ASSERT_VALUES_EQUAL(failed, TVector<TString>{});
+ }
+
+ Y_UNIT_TEST(TableHints) {
+ UNIT_ASSERT(SqlToYql("SELECT * FROM plato.Input WITH INFER_SCHEMA").IsOk());
+ UNIT_ASSERT(SqlToYql("SELECT * FROM plato.Input WITH (INFER_SCHEMA)").IsOk());
+ }
+
+ Y_UNIT_TEST(InNoHints) {
+ TString query = "SELECT * FROM plato.Input WHERE key IN (1,2,3)";
+
+ VerifySqlInHints(query, { "'('('warnNoAnsi))" }, {});
+ VerifySqlInHints(query, { "'()" }, false);
+ VerifySqlInHints(query, { "'('('ansi))" }, true);
+ }
+
+ Y_UNIT_TEST(InHintCompact) {
+ // should parse COMPACT as hint
+ TString query = "SELECT * FROM plato.Input WHERE key IN COMPACT(1, 2, 3)";
+
+ VerifySqlInHints(query, { "'('isCompact)" });
+ }
+
+ Y_UNIT_TEST(InHintSubquery) {
+ // should parse tableSource as hint
+ TString query = "$subq = (SELECT key FROM plato.Input); SELECT * FROM plato.Input WHERE key IN $subq";
+
+ VerifySqlInHints(query, { "'('tableSource)" });
+ }
+
+ Y_UNIT_TEST(InHintCompactSubquery) {
+ TString query = "$subq = (SELECT key FROM plato.Input); SELECT * FROM plato.Input WHERE key IN COMPACT $subq";
+
+ VerifySqlInHints(query, { "'('isCompact)", "'('tableSource)" });
+ }
+
+ Y_UNIT_TEST(CompactKeywordNotReservedForNames) {
+ UNIT_ASSERT(SqlToYql("SELECT COMPACT FROM plato.Input WHERE COMPACT IN COMPACT(1, 2, 3)").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT * FROM COMPACT").IsOk());
+ }
+
+ Y_UNIT_TEST(FamilyKeywordNotReservedForNames) {
+ // FIXME: check if we can get old behaviour
+ //UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE FAMILY (FAMILY Uint32, PRIMARY KEY (FAMILY));").IsOk());
+ //UNIT_ASSERT(SqlToYql("USE plato; SELECT FAMILY FROM FAMILY").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT FAMILY FROM Input").IsOk());
+ }
+
+ Y_UNIT_TEST(ResetKeywordNotReservedForNames) {
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE RESET (RESET Uint32, PRIMARY KEY (RESET));").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT RESET FROM RESET").IsOk());
+ }
+
+ Y_UNIT_TEST(SyncKeywordNotReservedForNames) {
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE SYNC (SYNC Uint32, PRIMARY KEY (SYNC));").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT SYNC FROM SYNC").IsOk());
+ }
+
+ Y_UNIT_TEST(AsyncKeywordNotReservedForNames) {
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE ASYNC (ASYNC Uint32, PRIMARY KEY (ASYNC));").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT ASYNC FROM ASYNC").IsOk());
+ }
+
+ Y_UNIT_TEST(DisableKeywordNotReservedForNames) {
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE DISABLE (DISABLE Uint32, PRIMARY KEY (DISABLE));").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT DISABLE FROM DISABLE").IsOk());
+ }
+
+ Y_UNIT_TEST(ChangefeedKeywordNotReservedForNames) {
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE CHANGEFEED (CHANGEFEED Uint32, PRIMARY KEY (CHANGEFEED));").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT CHANGEFEED FROM CHANGEFEED").IsOk());
+ }
+
+ Y_UNIT_TEST(ReplicationKeywordNotReservedForNames) {
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE REPLICATION (REPLICATION Uint32, PRIMARY KEY (REPLICATION));").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT REPLICATION FROM REPLICATION").IsOk());
+ }
+
+ Y_UNIT_TEST(TransferKeywordNotReservedForNames) {
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE TRANSFER (TRANSFER Uint32, PRIMARY KEY (TRANSFER));").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT TRANSFER FROM TRANSFER").IsOk());
+ }
+
+ Y_UNIT_TEST(SecondsKeywordNotReservedForNames) {
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE SECONDS (SECONDS Uint32, PRIMARY KEY (SECONDS));").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT SECONDS FROM SECONDS").IsOk());
+ }
+
+ Y_UNIT_TEST(MillisecondsKeywordNotReservedForNames) {
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE MILLISECONDS (MILLISECONDS Uint32, PRIMARY KEY (MILLISECONDS));").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT MILLISECONDS FROM MILLISECONDS").IsOk());
+ }
+
+ Y_UNIT_TEST(MicrosecondsKeywordNotReservedForNames) {
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE MICROSECONDS (MICROSECONDS Uint32, PRIMARY KEY (MICROSECONDS));").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT MICROSECONDS FROM MICROSECONDS").IsOk());
+ }
+
+ Y_UNIT_TEST(NanosecondsKeywordNotReservedForNames) {
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE NANOSECONDS (NANOSECONDS Uint32, PRIMARY KEY (NANOSECONDS));").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT NANOSECONDS FROM NANOSECONDS").IsOk());
+ }
+
+ Y_UNIT_TEST(Jubilee) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; INSERT INTO Arcadia (r2000000) VALUES (\"2M GET!!!\");");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(QualifiedAsteriskBefore) {
+ NYql::TAstParseResult res = SqlToYql(
+ "PRAGMA DisableSimpleColumns;"
+ "select interested_table.*, LENGTH(value) AS megahelpful_len from plato.Input as interested_table;"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ static bool seenStar = false;
+ if (word == "FlattenMembers") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("interested_table."));
+ } else if (word == "SqlProjectItem") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("megahelpful_len")));
+ UNIT_ASSERT_VALUES_EQUAL(seenStar, true);
+ } else if (word == "SqlProjectStarItem") {
+ seenStar = true;
+ }
+ };
+ TWordCountHive elementStat = {{TString("FlattenMembers"), 0}, {TString("SqlProjectItem"), 0}, {TString("SqlProjectStarItem"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["FlattenMembers"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectStarItem"]);
+ }
+
+ Y_UNIT_TEST(QualifiedAsteriskAfter) {
+ NYql::TAstParseResult res = SqlToYql(
+ "PRAGMA DisableSimpleColumns;"
+ "select LENGTH(value) AS megahelpful_len, interested_table.* from plato.Input as interested_table;"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ static bool seenStar = false;
+ if (word == "FlattenMembers") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("interested_table."));
+ } else if (word == "SqlProjectItem") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("megahelpful_len")));
+ UNIT_ASSERT_VALUES_EQUAL(seenStar, false);
+ } else if (word == "SqlProjectStarItem") {
+ seenStar = true;
+ }
+ };
+ TWordCountHive elementStat = {{TString("FlattenMembers"), 0}, {TString("SqlProjectItem"), 0}, {TString("SqlProjectStarItem"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["FlattenMembers"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectStarItem"]);
+ }
+
+ Y_UNIT_TEST(QualifiedMembers) {
+ NYql::TAstParseResult res = SqlToYql("select interested_table.key, interested_table.value from plato.Input as interested_table;");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ const bool fieldKey = TString::npos != line.find(Quote("key"));
+ const bool fieldValue = TString::npos != line.find(Quote("value"));
+ const bool refOnTable = TString::npos != line.find("interested_table.");
+ if (word == "SqlProjectItem") {
+ UNIT_ASSERT(fieldKey || fieldValue);
+ UNIT_ASSERT(!refOnTable);
+ } else if (word == "Write!") {
+ UNIT_ASSERT(fieldKey && fieldValue && !refOnTable);
+ }
+ };
+ TWordCountHive elementStat = {{TString("SqlProjectStarItem"), 0}, {TString("SqlProjectItem"), 0}, {TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(0, elementStat["SqlProjectStarItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlProjectItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(ExplainQueryPlan) {
+ UNIT_ASSERT(SqlToYql("EXPLAIN SELECT 1;").IsOk());
+ UNIT_ASSERT(SqlToYql("EXPLAIN QUERY PLAN SELECT 1;").IsOk());
+ }
+
+ Y_UNIT_TEST(JoinParseCorrect) {
+ NYql::TAstParseResult res = SqlToYql(
+ "PRAGMA DisableSimpleColumns;"
+ " SELECT table_bb.*, table_aa.key as megakey"
+ " FROM plato.Input AS table_aa"
+ " JOIN plato.Input AS table_bb"
+ " ON table_aa.value == table_bb.value;"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "SelectMembers") {
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("table_aa."));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table_bb."));
+ } else if (word == "SqlProjectItem") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("megakey")));
+ } else if (word == "SqlColumn") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("table_aa")));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("key")));
+ }
+ };
+ TWordCountHive elementStat = {{TString("SqlProjectItem"), 0}, {TString("SqlProjectStarItem"), 0}, {TString("SelectMembers"), 0}, {TString("SqlColumn"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectStarItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SelectMembers"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlColumn"]);
+ }
+
+ Y_UNIT_TEST(Join3Table) {
+ NYql::TAstParseResult res = SqlToYql(
+ " PRAGMA DisableSimpleColumns;"
+ " SELECT table_bb.*, table_aa.key as gigakey, table_cc.* "
+ " FROM plato.Input AS table_aa"
+ " JOIN plato.Input AS table_bb ON table_aa.key == table_bb.key"
+ " JOIN plato.Input AS table_cc ON table_aa.subkey == table_cc.subkey;"
+ );
+ Err2Str(res);
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "SelectMembers") {
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("table_aa."));
+ UNIT_ASSERT(line.find("table_bb.") != TString::npos || line.find("table_cc.") != TString::npos);
+ } else if (word == "SqlProjectItem") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("gigakey")));
+ } else if (word == "SqlColumn") {
+ const auto posTableAA = line.find(Quote("table_aa"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, posTableAA);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("key")));
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("table_aa", posTableAA + 3));
+ }
+ };
+ TWordCountHive elementStat = {{TString("SqlProjectItem"), 0}, {TString("SqlProjectStarItem"), 0}, {TString("SelectMembers"), 0}, {TString("SqlColumn"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlProjectStarItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SelectMembers"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlColumn"]);
+ }
+
+ Y_UNIT_TEST(DisabledJoinCartesianProduct) {
+ NYql::TAstParseResult res = SqlToYql("pragma DisableAnsiImplicitCrossJoin; use plato; select * from A,B,C");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:67: Error: Cartesian product of tables is disabled. Please use explicit CROSS JOIN or enable it via PRAGMA AnsiImplicitCrossJoin\n");
+ }
+
+ Y_UNIT_TEST(JoinCartesianProduct) {
+ NYql::TAstParseResult res = SqlToYql("pragma AnsiImplicitCrossJoin; use plato; select * from A,B,C");
+ UNIT_ASSERT(res.Root);
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "EquiJoin") {
+ auto pos = line.find("Cross");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, pos);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Cross", pos + 1));
+ }
+ };
+ TWordCountHive elementStat = {{TString("EquiJoin"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["EquiJoin"]);
+ }
+
+ Y_UNIT_TEST(CreateAlterUserWithLoginNoLogin) {
+ auto reqCreateUser = SqlToYql(R"(
+ USE plato;
+ CREATE USER user1;
+ )");
+
+ UNIT_ASSERT(reqCreateUser.IsOk());
+
+ auto reqAlterUser = SqlToYql(R"(
+ USE plato;
+ ALTER USER user1;
+ )");
+
+ UNIT_ASSERT(!reqAlterUser.IsOk());
+#if ANTLR_VER == 3
+ UNIT_ASSERT_STRING_CONTAINS(reqAlterUser.Issues.ToString(), "Error: Unexpected token ';' : cannot match to any predicted input...");
+#else
+ UNIT_ASSERT_STRING_CONTAINS(reqAlterUser.Issues.ToString(), "Error: mismatched input ';' expecting {ENCRYPTED, HASH, LOGIN, NOLOGIN, PASSWORD, RENAME, WITH}");
+#endif
+
+ auto reqPasswordAndLogin = SqlToYql(R"(
+ USE plato;
+ CREATE USER user1 LOgin;
+ )");
+
+ UNIT_ASSERT(reqPasswordAndLogin.IsOk());
+
+ auto reqPasswordAndNoLogin = SqlToYql(R"(
+ USE plato;
+ CREATE USER user1 PASSWORD '123' NOLOGIN;
+ )");
+
+ UNIT_ASSERT(reqPasswordAndNoLogin.IsOk());
+
+ auto reqLogin = SqlToYql(R"(
+ USE plato;
+ CREATE USER user1 LOGIN;
+ )");
+
+ UNIT_ASSERT(reqLogin.IsOk());
+
+ auto reqNoLogin = SqlToYql(R"(
+ USE plato;
+ CREATE USER user1 NOLOGIN;
+ )");
+
+ UNIT_ASSERT(reqNoLogin.IsOk());
+
+ auto reqLoginNoLogin = SqlToYql(R"(
+ USE plato;
+ CREATE USER user1 LOGIN NOLOGIN;
+ )");
+
+ UNIT_ASSERT(!reqLoginNoLogin.IsOk());
+ UNIT_ASSERT_STRING_CONTAINS(reqLoginNoLogin.Issues.ToString(), "Error: Conflicting or redundant options");
+
+ auto reqAlterLoginNoLogin = SqlToYql(R"(
+ USE plato;
+ CREATE USER user1 LOGIN;
+ ALTER USER user1 NOLOGIN;
+ )");
+
+ UNIT_ASSERT(reqAlterLoginNoLogin.IsOk());
+
+ auto reqAlterLoginNoLoginWithPassword = SqlToYql(R"(
+ USE plato;
+ CREATE USER user1 LOGIN;
+ ALTER USER user1 PASSWORD '321' NOLOGIN;
+ )");
+
+ UNIT_ASSERT(reqAlterLoginNoLoginWithPassword.IsOk());
+ }
+
+ Y_UNIT_TEST(CreateUserWithHash) {
+ auto reqCreateUser = SqlToYql(R"(
+ USE plato;
+ CREATE USER user1 HASH '{
+ "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
+ "salt": "U+tzBtgo06EBQCjlARA6Jg==",
+ "type": "argon2id"
+ }';
+ )");
+
+ UNIT_ASSERT(reqCreateUser.IsOk());
+
+ auto reqCreateUserWithNoLogin = SqlToYql(R"(
+ USE plato;
+ CREATE USER user1 HASH '{
+ "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
+ "salt": "U+tzBtgo06EBQCjlARA6Jg==",
+ "type": "argon2id"
+ }'
+ NOLOGIN;
+ )");
+
+ UNIT_ASSERT(reqCreateUserWithNoLogin.IsOk());
+
+ auto reqCreateUserWithPassword = SqlToYql(R"(
+ USE plato;
+ CREATE USER user1 HASH '{
+ "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
+ "salt": "U+tzBtgo06EBQCjlARA6Jg==",
+ "type": "argon2id"
+ }'
+ PASSWORD '123';
+ )");
+
+ UNIT_ASSERT(!reqCreateUserWithPassword.IsOk());
+ UNIT_ASSERT_STRING_CONTAINS(reqCreateUserWithPassword.Issues.ToString(), "Error: Conflicting or redundant options");
+
+ auto reqAlterUser = SqlToYql(R"(
+ USE plato;
+ CREATE USER user1;
+ ALTER USER user1 HASH '{
+ "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
+ "salt": "U+tzBtgo06EBQCjlARA6Jg==",
+ "type": "argon2id"
+ }';
+ )");
+
+ UNIT_ASSERT(reqAlterUser.IsOk());
+ }
+
+ Y_UNIT_TEST(CreateUserQoutas) {
+ {
+ auto req = SqlToYql(R"(
+ use plato;
+ CREATE USER user1 PASSWORD passwd;
+ )");
+
+#if ANTLR_VER == 3
+ TString error = "<main>:3:43: Error: Unexpected token 'passwd' : unexpected input : nothing is expected here\n\n";
+#else
+ TString error = "<main>:3:43: Error: mismatched input 'passwd' expecting {NULL, STRING_VALUE}\n";
+#endif
+ UNIT_ASSERT_VALUES_EQUAL(Err2Str(req), error);
+ UNIT_ASSERT(!req.Root);
+ }
+
+ {
+ auto req = SqlToYql(R"(
+ use plato;
+ CREATE USER user2 PASSWORD NULL;
+ )");
+
+ UNIT_ASSERT(req.Root);
+ }
+
+ {
+ auto req = SqlToYql(R"(
+ use plato;
+ CREATE USER user3 PASSWORD '';
+ )");
+
+ UNIT_ASSERT(req.Root);
+ }
+
+
+ {
+ auto req = SqlToYql(R"(
+ use plato;
+ CREATE USER user1 PASSWORD 'password1';
+ CREATE USER user2 PASSWORD 'password2';
+ CREATE USER user3;
+ )");
+
+ UNIT_ASSERT(req.Root);
+ }
+ }
+
+ Y_UNIT_TEST(JoinWithoutConcreteColumns) {
+ NYql::TAstParseResult res = SqlToYql(
+ " use plato;"
+ " SELECT a.v, b.value"
+ " FROM `Input1` VIEW `ksv` AS a"
+ " JOIN `Input2` AS b"
+ " ON a.k == b.key;"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "SqlProjectItem") {
+ UNIT_ASSERT(line.find(Quote("a.v")) != TString::npos || line.find(Quote("b.value")) != TString::npos);
+ } else if (word == "SqlColumn") {
+ const auto posTableA = line.find(Quote("a"));
+ const auto posTableB = line.find(Quote("b"));
+ if (posTableA != TString::npos) {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("v")));
+ } else {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, posTableB);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("value")));
+ }
+ }
+ };
+ TWordCountHive elementStat = {{TString("SqlProjectStarItem"), 0}, {TString("SqlProjectItem"), 0}, {TString("SqlColumn"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(0, elementStat["SqlProjectStarItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlProjectItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlColumn"]);
+ }
+
+ Y_UNIT_TEST(JoinWithSameValues) {
+ NYql::TAstParseResult res = SqlToYql("SELECT a.value, b.value FROM plato.Input AS a JOIN plato.Input as b ON a.key == b.key;");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "SqlProjectItem") {
+ const bool isValueFromA = TString::npos != line.find(Quote("a.value"));
+ const bool isValueFromB = TString::npos != line.find(Quote("b.value"));
+ UNIT_ASSERT(isValueFromA || isValueFromB);
+ } if (word == "Write!") {
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("a.a."));
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("b.b."));
+ }
+ };
+ TWordCountHive elementStat = {{TString("SqlProjectStarItem"), 0}, {TString("SqlProjectItem"), 0}, {"Write!", 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(0, elementStat["SqlProjectStarItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(2, elementStat["SqlProjectItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(SameColumnsForDifferentTables) {
+ NYql::TAstParseResult res = SqlToYql("SELECT a.key, b.key FROM plato.Input as a JOIN plato.Input as b on a.key==b.key;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(SameColumnsForDifferentTablesFullJoin) {
+ NYql::TAstParseResult res = SqlToYql("SELECT a.key, b.key, a.value, b.value FROM plato.Input AS a FULL JOIN plato.Input AS b USING(key);");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(JoinStreamLookupStrategyHint) {
+ {
+ NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ StreamLookup() */ plato.Input AS b USING(key);");
+ UNIT_ASSERT(res.Root);
+ }
+ //case insensitive
+ {
+ NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ streamlookup() */ plato.Input AS b USING(key);");
+ UNIT_ASSERT(res.Root);
+ }
+ }
+
+ Y_UNIT_TEST(JoinConflictingStrategyHint) {
+ {
+ NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ StreamLookup() */ /*+ Merge() */ plato.Input AS b USING(key);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:91: Error: Conflicting join strategy hints\n");
+ }
+ }
+
+ Y_UNIT_TEST(JoinDuplicatingStrategyHint) {
+ {
+ NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ StreamLookup() */ /*+ StreamLookup() */ plato.Input AS b USING(key);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:98: Error: Duplicate join strategy hint\n");
+ }
+ }
+
+ Y_UNIT_TEST(WarnCrossJoinStrategyHint) {
+ NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a CROSS JOIN /*+ merge() */ plato.Input AS b;");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:32: Warning: Non-default join strategy will not be used for CROSS JOIN, code: 4534\n");
+ }
+
+ Y_UNIT_TEST(WarnCartesianProductStrategyHint) {
+ NYql::TAstParseResult res = SqlToYql("pragma AnsiImplicitCrossJoin; use plato; SELECT * FROM A, /*+ merge() */ B;");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:74: Warning: Non-default join strategy will not be used for CROSS JOIN, code: 4534\n");
+ }
+
+ Y_UNIT_TEST(WarnUnknownJoinStrategyHint) {
+ NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input AS a JOIN /*+ xmerge() */ plato.Input AS b USING (key);");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_STRINGS_EQUAL(res.Issues.ToString(), "<main>:1:41: Warning: Unsupported join hint: xmerge, code: 4534\n");
+ }
+
+ Y_UNIT_TEST(ReverseLabels) {
+ NYql::TAstParseResult res = SqlToYql("select in.key as subkey, subkey as key from plato.Input as in;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(AutogenerationAliasWithoutCollisionConflict1) {
+ NYql::TAstParseResult res = SqlToYql("select LENGTH(Value), key as column1 from plato.Input;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(AutogenerationAliasWithoutCollision2Conflict2) {
+ NYql::TAstParseResult res = SqlToYql("select key as column0, LENGTH(Value) from plato.Input;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(InputAliasForQualifiedAsterisk) {
+ NYql::TAstParseResult res = SqlToYql("use plato; select zyuzya.*, key from plato.Input as zyuzya;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(SelectSupportsResultColumnsWithTrailingComma) {
+ NYql::TAstParseResult res = SqlToYql("select a, b, c, from plato.Input;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(SelectOrderByLabeledColumn) {
+ NYql::TAstParseResult res = SqlToYql("pragma DisableOrderedColumns; select key as goal from plato.Input order by goal");
+ UNIT_ASSERT(res.Root);
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "DataSource") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("plato"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Input"));
+
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("goal"));
+ } else if (word == "Sort") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("goal"));
+
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("key"));
+ }
+ };
+ TWordCountHive elementStat = {{TString("DataSource"), 0}, {TString("Sort"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["DataSource"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Sort"]);
+ }
+
+ Y_UNIT_TEST(SelectOrderBySimpleExpr) {
+ NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by a + a");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(SelectAssumeOrderByTableRowAccess) {
+ NYql::TAstParseResult res = SqlToYql("$key = 'foo';select * from plato.Input assume order by TableRow().$key");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(SelectOrderByDuplicateLabels) {
+ NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by a, a");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(SelectOrderByExpression) {
+ NYql::TAstParseResult res = SqlToYql("select * from plato.Input as i order by cast(key as uint32) + cast(subkey as uint32)");
+ UNIT_ASSERT(res.Root);
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Sort") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"+MayWarn\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("key"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("subkey"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Bool 'true)"));
+
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("i.key"));
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("i.subkey"));
+ }
+ };
+ TWordCountHive elementStat = {{TString("Sort"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Sort"]);
+ }
+
+ Y_UNIT_TEST(SelectOrderByExpressionDesc) {
+ NYql::TAstParseResult res = SqlToYql("pragma disablesimplecolumns; select i.*, key, subkey from plato.Input as i order by cast(i.key as uint32) - cast(i.subkey as uint32) desc");
+ UNIT_ASSERT(res.Root);
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Sort") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"-MayWarn\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"key\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"subkey\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Bool 'false)"));
+ } else if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'columns"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"key\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"subkey\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("prefix"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"i.\""));
+ }
+ };
+ TWordCountHive elementStat = {{TString("Sort"), 0}, {TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Sort"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(SelectOrderByExpressionAsc) {
+ NYql::TAstParseResult res = SqlToYql("select i.key, i.subkey from plato.Input as i order by cast(key as uint32) % cast(i.subkey as uint32) asc");
+ UNIT_ASSERT(res.Root);
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Sort") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"%MayWarn\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"key\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"subkey\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Bool 'true)"));
+ } else if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'columns"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"key\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"subkey\""));
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("i."));
+ }
+ };
+ TWordCountHive elementStat = {{TString("Sort"), 0}, {TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Sort"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(ReferenceToKeyInSubselect) {
+ NYql::TAstParseResult res = SqlToYql("select b.key from (select a.key from plato.Input as a) as b;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(OrderByCastValue) {
+ NYql::TAstParseResult res = SqlToYql("select i.key, i.subkey from plato.Input as i order by cast(key as uint32) desc;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(GroupByCastValue) {
+ NYql::TAstParseResult res = SqlToYql("select count(1) from plato.Input as i group by cast(key as uint8);");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(KeywordInSelectColumns) {
+ NYql::TAstParseResult res = SqlToYql("select in, s.check from (select 1 as in, \"test\" as check) as s;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(SelectAllGroupBy) {
+ NYql::TAstParseResult res = SqlToYql("select * from plato.Input group by subkey;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(CreateObjectWithFeatures) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('\"K2\" '\"V2\") '('\"Key1\" '\"Value1\")"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
+ }
+
+ Y_UNIT_TEST(CreateObjectIfNotExists) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT IF NOT EXISTS secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObjectIfNotExists"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
+ }
+
+ Y_UNIT_TEST(CreateObjectWithFeaturesStrings) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET) WITH (Key1=\"Value1\", K2='V2', K3=V3, K4='', K5=`aaa`, K6='a\\'aa');");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('\"K2\" '\"V2\") '('\"K3\" '\"V3\") '('\"K4\" '\"\") '('\"K5\" '\"aaa\") '('\"K6\" '\"a'aa\") '('\"Key1\" '\"Value1\")"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}, {TString("SECRET"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ }
+
+ Y_UNIT_TEST(UpsertObjectWithFeatures) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; UPSERT OBJECT secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('\"K2\" '\"V2\") '('\"Key1\" '\"Value1\")"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("upsertObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
+ }
+
+ Y_UNIT_TEST(CreateObjectWithFeaturesAndFlags) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2, RECURSE);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('\"Key1\" '\"Value1\") '('\"RECURSE\")"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
+ }
+
+ Y_UNIT_TEST(Select1Type) {
+ NYql::TAstParseResult res = SqlToYql("SELECT 1 type;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(SelectTableType) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; SELECT * from T type;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(CreateObjectNoFeatures) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
+ }
+
+ Y_UNIT_TEST(AlterObjectWithFeatures) {
+ NYql::TAstParseResult res = SqlToYql(
+ "USE plato;\n"
+ "declare $path as String;\n"
+ "ALTER OBJECT secretId (TYPE SECRET) SET (Key1=$path, K2=V2);"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'features"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"Key1\" (EvaluateAtom \"$path\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"K2\" '\"V2\""));
+
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alterObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
+ }
+
+ Y_UNIT_TEST(AlterObjectNoFeatures) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER OBJECT secretId (TYPE SECRET);");
+ UNIT_ASSERT(!res.Root);
+ }
+
+ Y_UNIT_TEST(DropObjectNoFeatures) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT secretId (TYPE SECRET);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
+ }
+
+ Y_UNIT_TEST(DropObjectWithFeatures) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT secretId (TYPE SECRET) WITH (A, B, C);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'features"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
+ }
+
+ Y_UNIT_TEST(DropObjectWithOneOption) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT secretId (TYPE SECRET) WITH OVERRIDE;");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'features"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"OVERRIDE\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
+ }
+
+ Y_UNIT_TEST(DropObjectIfExists) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT IF EXISTS secretId (TYPE SECRET);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObjectIfExists"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]);
+ }
+
+ Y_UNIT_TEST(PrimaryKeyParseCorrect) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE tableName (Key Uint32, Subkey Int64, Value String, PRIMARY KEY (Key, Subkey));");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"Key\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"Subkey\""));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}, {TString("primarykey"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["primarykey"]);
+ }
+
+ Y_UNIT_TEST(AlterDatabaseAst) {
+ NYql::TAstParseResult request = SqlToYql("USE plato; ALTER DATABASE `/Root/test` OWNER TO user1;");
+ UNIT_ASSERT(request.IsOk());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(
+ R"(let world (Write! world sink (Key '('databasePath (String '"/Root/test"))) (Void) '('('mode 'alterDatabase) '('owner '"user1"))))"
+ ));
+ };
+
+ TWordCountHive elementStat({TString("\'mode \'alterDatabase")});
+ VerifyProgram(request, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["\'mode \'alterDatabase"]);
+ }
+
+ Y_UNIT_TEST(CreateTableNonNullableYqlTypeAstCorrect) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32 not null);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (DataType 'Int32) '('columnConstrains '('('not_null))) '())))))))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(CreateTableNullableYqlTypeAstCorrect) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))))))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(CreateTableNonNullablePgTypeAstCorrect) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a pg_int4 not null);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (PgType '_int4) '('columnConstrains '('('not_null))) '())))))))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(CreateTableNullablePgTypeAstCorrect) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a pg_int4);");
+ UNIT_ASSERT(res.Root);
+
+ res.Root->PrettyPrintTo(Cout, PRETTY_FLAGS);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (PgType '_int4)) '('columnConstrains '()) '()))))))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(CreateTableNullPkColumnsAreAllowed) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32, primary key(a));");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")))))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(CreateTableNotNullPkColumnsAreIdempotentAstCorrect) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32 not null, primary key(a));");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (DataType 'Int32) '('columnConstrains '('('not_null))) '()))) '('primarykey '('"a"))))))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(CreateTableWithIfNotExists) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE IF NOT EXISTS t (a int32, primary key(a));");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create_if_not_exists) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")))))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(CreateTempTable) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TEMP TABLE t (a int32, primary key(a));");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos,
+ line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")) '('temporary))))__"), line);
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(CreateTemporaryTable) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TEMPORARY TABLE t (a int32, primary key(a));");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos,
+ line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")) '('temporary))))__"), line);
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ }
+
+ Y_UNIT_TEST(CreateTableWithoutTypes) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a, primary key(a));");
+ UNIT_ASSERT(!res.Root);
+ }
+
+ Y_UNIT_TEST(CreateTableAsSelectWithTypes) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32, primary key(a)) AS SELECT * FROM ts;");
+ UNIT_ASSERT(!res.Root);
+ }
+
+ Y_UNIT_TEST(CreateTableAsSelect) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a, b, primary key(a)) AS SELECT * FROM ts;");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((let world (Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '('('"a") '('"b"))) '('primarykey '('"a"))))))__"));
+ }
+ if (word == "Read!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((Read! world (DataSource '"yt" '"plato") (MrTableConcat (Key '('table (String '"ts")))))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write!"), 0}, {TString("Read!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Read!"]);
+ }
+
+ Y_UNIT_TEST(CreateTableAsSelectOnlyPrimary) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (primary key(a)) AS SELECT * FROM ts;");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((let world (Write! world sink (Key '('tablescheme (String '"t"))) values '('('mode 'create) '('columns '()) '('primarykey '('"a"))))))__"));
+ }
+ if (word == "Read!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((Read! world (DataSource '"yt" '"plato") (MrTableConcat (Key '('table (String '"ts")))))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write!"), 0}, {TString("Read!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Read!"]);
+ }
+
+ Y_UNIT_TEST(CreateTableAsValuesFail) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a, primary key(a)) AS VALUES (1), (2);");
+ UNIT_ASSERT(!res.Root);
+ }
+
+ Y_UNIT_TEST(CreateTableDuplicatedPkColumnsFail) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32 not null, primary key(a, a));");
+ UNIT_ASSERT(!res.Root);
+ }
+
+ Y_UNIT_TEST(DeleteFromTableByKey) {
+ NYql::TAstParseResult res = SqlToYql("delete from plato.Input where key = 200;", 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete)"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DeleteFromTable) {
+ NYql::TAstParseResult res = SqlToYql("delete from plato.Input;", 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete)"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DeleteFromTableBatch) {
+ NYql::TAstParseResult res = SqlToYql("batch delete from plato.Input;", 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete)"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('is_batch 'true)"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DeleteFromTableOnValues) {
+ NYql::TAstParseResult res = SqlToYql("delete from plato.Input on (key) values (1);",
+ 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete_on)"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DeleteFromTableOnSelect) {
+ NYql::TAstParseResult res = SqlToYql(
+ "delete from plato.Input on select key from plato.Input where value > 0;", 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete_on)"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DeleteFromTableOnBatch) {
+ NYql::TAstParseResult res = SqlToYql("batch delete from plato.Input on (key) values (1);",
+ 10, "kikimr");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:6: Error: BATCH DELETE is unsupported with ON\n");
+ }
+
+ Y_UNIT_TEST(UpdateByValues) {
+ NYql::TAstParseResult res = SqlToYql("update plato.Input set key = 777, value = 'cool' where key = 200;", 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)"));
+ } else if (word == "AsStruct") {
+ const bool isKey = line.find("key") != TString::npos;
+ const bool isValue = line.find("value") != TString::npos;
+ UNIT_ASSERT(isKey || isValue);
+ if (isKey) {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("777")));
+ } else if (isValue) {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("cool")));
+ }
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}, {TString("AsStruct"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AsStruct"]);
+ }
+
+ Y_UNIT_TEST(UpdateByValuesBatch) {
+ NYql::TAstParseResult res = SqlToYql("batch update plato.Input set key = 777, value = 'cool' where key = 200;", 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('is_batch 'true)"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(UpdateByMultiValues) {
+ NYql::TAstParseResult res = SqlToYql("update plato.Input set (key, value, subkey) = ('2','ddd',':') where key = 200;", 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)"));
+ } else if (word == "AsStruct") {
+ const bool isKey = line.find("key") != TString::npos;
+ const bool isSubkey = line.find("subkey") != TString::npos;
+ const bool isValue = line.find("value") != TString::npos;
+ UNIT_ASSERT(isKey || isSubkey || isValue);
+ if (isKey && !isSubkey) {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("2")));
+ } else if (isSubkey) {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote(":")));
+ } else if (isValue) {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("ddd")));
+ }
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}, {TString("AsStruct"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AsStruct"]);
+ }
+
+ Y_UNIT_TEST(UpdateBySelect) {
+ NYql::TAstParseResult res = SqlToYql("update plato.Input set (key, value, subkey) = (select key, value, subkey from plato.Input where key = 911) where key = 200;", 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ int lineIndex = 0;
+ int writeLineIndex = -1;
+ bool found = false;
+
+ TVerifyLineFunc verifyLine = [&lineIndex, &writeLineIndex, &found](const TString& word, const TString& line) {
+ if (word == "Write") {
+ writeLineIndex = lineIndex;
+ found = line.find("('mode 'update)") != TString::npos;
+ } else if (word == "mode") {
+ found |= lineIndex == writeLineIndex + 1 && line.find("('mode 'update)") != TString::npos;
+ UNIT_ASSERT(found);
+ }
+
+ ++lineIndex;
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}, {TString("mode"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(UpdateSelfModifyAll) {
+ NYql::TAstParseResult res = SqlToYql("update plato.Input set subkey = subkey + 's';", 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)"));
+ } else if (word == "AsStruct") {
+ const bool isSubkey = line.find("subkey") != TString::npos;
+ UNIT_ASSERT(isSubkey);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("subkey")));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(Quote("s")));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}, {TString("AsStruct"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AsStruct"]);
+ }
+
+ Y_UNIT_TEST(UpdateOnValues) {
+ NYql::TAstParseResult res = SqlToYql("update plato.Input on (key, value) values (5, 'cool')", 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update_on)"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(UpdateOnSelect) {
+ NYql::TAstParseResult res = SqlToYql(
+ "update plato.Input on select key, value + 1 as value from plato.Input", 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update_on)"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(UpdateOnBatch) {
+ NYql::TAstParseResult res = SqlToYql("batch update plato.Input on (key, value) values (5, 'cool')", 10, "kikimr");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:6: Error: BATCH UPDATE is unsupported with ON\n");
+ }
+
+ Y_UNIT_TEST(UnionAllTest) {
+ NYql::TAstParseResult res = SqlToYql("PRAGMA DisableEmitUnionMerge; SELECT key FROM plato.Input UNION ALL select subkey FROM plato.Input;");
+ UNIT_ASSERT(res.Root);
+
+ TWordCountHive elementStat = {{TString("UnionAll"), 0}};
+ VerifyProgram(res, elementStat, {});
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["UnionAll"]);
+ }
+
+ Y_UNIT_TEST(UnionAllMergeTest) {
+ NYql::TAstParseResult res = SqlToYql("PRAGMA EmitUnionMerge; SELECT key FROM plato.Input UNION ALL select subkey FROM plato.Input;");
+ UNIT_ASSERT(res.Root);
+
+ TWordCountHive elementStat = {{TString("UnionMerge"), 0}};
+ VerifyProgram(res, elementStat, {});
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["UnionMerge"]);
+ }
+
+ Y_UNIT_TEST(UnionTest) {
+ NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input UNION select subkey FROM plato.Input;");
+ UNIT_ASSERT(res.Root);
+
+ TWordCountHive elementStat = {{TString("Union"), 0}};
+ VerifyProgram(res, elementStat, {});
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Union"]);
+ }
+
+ Y_UNIT_TEST(UnionAggregationTest) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ PRAGMA DisableEmitUnionMerge;
+ SELECT 1
+ UNION ALL
+ SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
+ UNION
+ SELECT 1 UNION SELECT 1 UNION SELECT 1 UNION SELECT 1
+ UNION ALL
+ SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1;
+ )");
+ UNIT_ASSERT(res.Root);
+
+ TWordCountHive elementStat = {{TString("Union"), 0}, {TString("UnionAll"), 0}};
+ VerifyProgram(res, elementStat, {});
+ UNIT_ASSERT_VALUES_EQUAL(2, elementStat["UnionAll"]);
+ UNIT_ASSERT_VALUES_EQUAL(3, elementStat["Union"]);
+ }
+
+ Y_UNIT_TEST(UnionMergeAggregationTest) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ PRAGMA EmitUnionMerge;
+ SELECT 1
+ UNION ALL
+ SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
+ UNION
+ SELECT 1 UNION SELECT 1 UNION SELECT 1 UNION SELECT 1
+ UNION ALL
+ SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1;
+ )");
+ UNIT_ASSERT(res.Root);
+
+ TWordCountHive elementStat = {{TString("Union"), 0}, {TString("UnionMerge"), 0}};
+ VerifyProgram(res, elementStat, {});
+ UNIT_ASSERT_VALUES_EQUAL(2, elementStat["UnionMerge"]);
+ UNIT_ASSERT_VALUES_EQUAL(3, elementStat["Union"]);
+ }
+
+ Y_UNIT_TEST(DeclareDecimalParameter) {
+ NYql::TAstParseResult res = SqlToYql("declare $value as Decimal(22,9); select $value as cnt;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(SimpleGroupBy) {
+ NYql::TAstParseResult res = SqlToYql("select count(1),z from plato.Input group by key as z order by z;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(EmptyColumnName0) {
+ /// Now it's parsed well and error occur on validate step like "4:31:Empty struct member name is not allowed" in "4:31:Function: AddMember"
+ NYql::TAstParseResult res = SqlToYql("insert into plato.Output (``, list1) values (0, AsList(0, 1, 2));");
+ /// Verify that parsed well without crash
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(KikimrRollback) {
+ NYql::TAstParseResult res = SqlToYql("use plato; select * from Input; rollback;", 10, "kikimr");
+ UNIT_ASSERT(res.Root);
+
+ TWordCountHive elementStat = {{TString("rollback"), 0}};
+ VerifyProgram(res, elementStat);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["rollback"]);
+ }
+
+ Y_UNIT_TEST(PragmaFile) {
+ NYql::TAstParseResult res = SqlToYql(R"(pragma file("HW", "sbr:181041334");)");
+ UNIT_ASSERT(res.Root);
+
+ TWordCountHive elementStat = {{TString(R"((let world (Configure! world (DataSource '"config") '"AddFileByUrl" '"HW" '"sbr:181041334")))"), 0}};
+ VerifyProgram(res, elementStat);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat.cbegin()->second);
+ }
+
+ Y_UNIT_TEST(DoNotCrashOnNamedInFilter) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; $all = ($table_name) -> { return true; }; SELECT * FROM FILTER(Input, $all)");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(PragmasFileAndUdfOrder) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ PRAGMA file("libvideoplayers_udf.so", "https://proxy.sandbox.yandex-team.ru/235185290");
+ PRAGMA udf("libvideoplayers_udf.so");
+ )");
+ UNIT_ASSERT(res.Root);
+
+ const auto programm = GetPrettyPrint(res);
+ const auto file = programm.find("AddFileByUrl");
+ const auto udfs = programm.find("ImportUdfs");
+ UNIT_ASSERT(file < udfs);
+ }
+
+ Y_UNIT_TEST(ProcessUserType) {
+ NYql::TAstParseResult res = SqlToYql("process plato.Input using Kikimr::PushData(TableRows());", 1, TString(NYql::KikimrProviderName));
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Kikimr.PushData") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("TupleType"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("TypeOf"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Kikimr.PushData"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Kikimr.PushData"]);
+ }
+
+ Y_UNIT_TEST(ProcessUserTypeAuth) {
+ NYql::TAstParseResult res = SqlToYql("process plato.Input using YDB::PushData(TableRows(), AsTuple('oauth', SecureParam('api:oauth')));", 1, TString(NYql::KikimrProviderName));
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "YDB.PushData") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("TupleType"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("TypeOf"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("api:oauth"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("YDB.PushData"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["YDB.PushData"]);
+ }
+
+ Y_UNIT_TEST(SelectStreamRtmr) {
+ NYql::TAstParseResult res = SqlToYql(
+ "USE plato; INSERT INTO Output SELECT STREAM key FROM Input;",
+ 10, TString(NYql::RtmrProviderName));
+ UNIT_ASSERT(res.Root);
+
+ res = SqlToYql(
+ "USE plato; INSERT INTO Output SELECT key FROM Input;",
+ 10, TString(NYql::RtmrProviderName));
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(SelectStreamRtmrJoinWithYt) {
+ NYql::TAstParseResult res = SqlToYql(
+ "USE plato; INSERT INTO Output SELECT STREAM key FROM Input LEFT JOIN hahn.ttt as t ON Input.key = t.Name;",
+ 10, TString(NYql::RtmrProviderName));
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(SelectStreamNonRtmr) {
+ NYql::TAstParseResult res = SqlToYql(
+ "USE plato; INSERT INTO Output SELECT STREAM key FROM Input;",
+ 10);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:31: Error: SELECT STREAM is unsupported for non-streaming sources\n");
+ }
+
+ Y_UNIT_TEST(GroupByHopRtmr) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato; INSERT INTO Output SELECT key, SUM(value) AS value FROM Input
+ GROUP BY key, HOP(subkey, "PT10S", "PT30S", "PT20S");
+ )", 10, TString(NYql::RtmrProviderName));
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(GroupByHopRtmrSubquery) {
+ // 'use plato' intentially avoided
+ NYql::TAstParseResult res = SqlToYql(R"(
+ SELECT COUNT(*) AS value FROM (SELECT * FROM plato.Input)
+ GROUP BY HOP(Data, "PT10S", "PT30S", "PT20S")
+ )", 10, TString(NYql::RtmrProviderName));
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(GroupByHopRtmrSubqueryBinding) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+ $q = SELECT * FROM Input;
+ INSERT INTO Output SELECT STREAM * FROM (
+ SELECT COUNT(*) AS value FROM $q
+ GROUP BY HOP(Data, "PT10S", "PT30S", "PT20S")
+ );
+ )", 10, TString(NYql::RtmrProviderName));
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(GroupByNoHopRtmr) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato; INSERT INTO Output SELECT STREAM key, SUM(value) AS value FROM Input
+ GROUP BY key;
+ )", 10, TString(NYql::RtmrProviderName));
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:22: Error: Streaming group by query must have a hopping window specification.\n");
+ }
+
+ Y_UNIT_TEST(KikimrInserts) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+ INSERT INTO Output SELECT key, value FROM Input;
+ INSERT OR ABORT INTO Output SELECT key, value FROM Input;
+ INSERT OR IGNORE INTO Output SELECT key, value FROM Input;
+ INSERT OR REVERT INTO Output SELECT key, value FROM Input;
+ )", 10, TString(NYql::KikimrProviderName));
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(WarnMissingIsBeforeNotNull) {
+ NYql::TAstParseResult res = SqlToYql("select 1 NOT NULL");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Warning: Missing IS keyword before NOT NULL, code: 4507\n");
+ }
+
+ Y_UNIT_TEST(Subqueries) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+ $sq1 = (SELECT * FROM plato.Input);
+
+ $sq2 = SELECT * FROM plato.Input;
+
+ $squ1 = (
+ SELECT * FROM plato.Input
+ UNION ALL
+ SELECT * FROM plato.Input
+ );
+
+ $squ2 =
+ SELECT * FROM plato.Input
+ UNION ALL
+ SELECT * FROM plato.Input;
+
+ $squ3 = (
+ (SELECT * FROM plato.Input)
+ UNION ALL
+ (SELECT * FROM plato.Input)
+ );
+
+ SELECT * FROM $sq1;
+ SELECT * FROM $sq2;
+ SELECT * FROM $squ1;
+ SELECT * FROM $squ2;
+ SELECT * FROM $squ3;
+ )");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(SubqueriesJoin) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+
+ $left = SELECT * FROM plato.Input1 WHERE value != "BadValue";
+ $right = SELECT * FROM plato.Input2;
+
+ SELECT * FROM $left AS l
+ JOIN $right AS r
+ ON l.key == r.key;
+ )");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(AnyInBackticksAsTableName) {
+ NYql::TAstParseResult res = SqlToYql("use plato; select * from `any`;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(AnyJoinForTableAndSubQuery) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+
+ $r = SELECT * FROM plato.Input2;
+
+ SELECT * FROM ANY plato.Input1 AS l
+ LEFT JOIN ANY $r AS r
+ USING (key);
+ )");
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "EquiJoin") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('left 'any)"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('right 'any)"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("left"), 0}, {TString("right"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["left"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["right"]);
+ }
+
+ Y_UNIT_TEST(AnyJoinForTableAndTableSource) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+
+ $r = AsList(
+ AsStruct("aaa" as key, "bbb" as subkey, "ccc" as value)
+ );
+
+ SELECT * FROM ANY plato.Input1 AS l
+ LEFT JOIN ANY AS_TABLE($r) AS r
+ USING (key);
+ )");
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "EquiJoin") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('left 'any)"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('right 'any)"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("left"), 0}, {TString("right"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["left"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["right"]);
+ }
+
+ Y_UNIT_TEST(AnyJoinNested) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+
+ FROM ANY Input1 as a
+ JOIN Input2 as b ON a.key = b.key
+ LEFT JOIN ANY Input3 as c ON a.key = c.key
+ RIGHT JOIN ANY Input4 as d ON d.key = b.key
+ CROSS JOIN Input5
+ SELECT *;
+ )");
+
+ UNIT_ASSERT(res.Root);
+
+ TWordCountHive elementStat = {{TString("left"), 0}, {TString("right"), 0}};
+ VerifyProgram(res, elementStat);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["left"]);
+ UNIT_ASSERT_VALUES_EQUAL(2, elementStat["right"]);
+ }
+
+ Y_UNIT_TEST(InlineAction) {
+ NYql::TAstParseResult res = SqlToYql(
+ "do begin\n"
+ " select 1\n"
+ "; end do\n");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "");
+ }
+
+ Y_UNIT_TEST(FlattenByCorrelationName) {
+ UNIT_ASSERT(SqlToYql("select * from plato.Input as t flatten by t.x").IsOk());
+ UNIT_ASSERT(SqlToYql("select * from plato.Input as t flatten by t -- same as flatten by t.t").IsOk());
+ }
+
+ Y_UNIT_TEST(DiscoveryMode) {
+ UNIT_ASSERT(SqlToYqlWithMode("insert into plato.Output select * from plato.Input", NSQLTranslation::ESqlMode::DISCOVERY).IsOk());
+ UNIT_ASSERT(SqlToYqlWithMode("select * from plato.concat(Input1, Input2)", NSQLTranslation::ESqlMode::DISCOVERY).IsOk());
+ UNIT_ASSERT(SqlToYqlWithMode("select * from plato.each(AsList(\"Input1\", \"Input2\"))", NSQLTranslation::ESqlMode::DISCOVERY).IsOk());
+ }
+
+ Y_UNIT_TEST(CubeWithAutoGeneratedLikeColumnName) {
+ UNIT_ASSERT(SqlToYql("select key,subkey,group from plato.Input group by cube(key,subkey,group)").IsOk());
+ }
+
+ Y_UNIT_TEST(CubeWithAutoGeneratedLikeAlias) {
+ UNIT_ASSERT(SqlToYql("select key,subkey,group from plato.Input group by cube(key,subkey,value as group)").IsOk());
+ }
+
+ Y_UNIT_TEST(FilterCanBeUsedAsColumnIdOrBind) {
+ UNIT_ASSERT(SqlToYql("select filter from plato.Input").IsOk());
+ UNIT_ASSERT(SqlToYql("select 1 as filter").IsOk());
+ UNIT_ASSERT(SqlToYql("$filter = 1; select $filter").IsOk());
+ }
+
+ Y_UNIT_TEST(DuplicateSemicolonsAreAllowedBetweenTopLevelStatements) {
+ UNIT_ASSERT(SqlToYql(";;select 1; ; select 2;/*comment*/;select 3;;--comment\n;select 4;;").IsOk());
+ }
+
+ Y_UNIT_TEST(DuplicateAndMissingTrailingSemicolonsAreAllowedBetweenActionStatements) {
+ TString req =
+ "define action $action($b,$c) as\n"
+ " ;;$d = $b + $c;\n"
+ " select $b;\n"
+ " select $c;;\n"
+ " select $d,\n"
+ "end define;\n"
+ "\n"
+ "do $action(1,2);";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(DuplicateAndMissingTrailingSemicolonsAreAllowedBetweenInlineActionStatements) {
+ TString req =
+ "do begin\n"
+ " ;select 1,\n"
+ "end do;\n"
+ "evaluate for $i in AsList(1,2,3) do begin\n"
+ " select $i;;\n"
+ " select $i + $i;;\n"
+ "end do;";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(DuplicateSemicolonsAreAllowedBetweenLambdaStatements) {
+ TString req =
+ "$x=1;\n"
+ "$foo = ($a, $b)->{\n"
+ " ;;$v = $a + $b;\n"
+ " $bar = ($c) -> {; return $c << $x};;\n"
+ " return $bar($v);;\n"
+ "};\n"
+ "select $foo(1,2);";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(StringLiteralWithEscapedBackslash) {
+ NYql::TAstParseResult res1 = SqlToYql(R"foo(SELECT 'a\\';)foo");
+ NYql::TAstParseResult res2 = SqlToYql(R"foo(SELECT "a\\";)foo");
+ UNIT_ASSERT(res1.Root);
+ UNIT_ASSERT(res2.Root);
+
+ TWordCountHive elementStat = {{TString("a\\"), 0}};
+
+ VerifyProgram(res1, elementStat);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["a\\"]);
+
+ VerifyProgram(res2, elementStat);
+ UNIT_ASSERT_VALUES_EQUAL(2, elementStat["a\\"]);
+ }
+
+ Y_UNIT_TEST(StringMultiLineLiteralWithEscapes) {
+ UNIT_ASSERT(SqlToYql("SELECT @@@foo@@@@bar@@@").IsOk());
+ UNIT_ASSERT(SqlToYql("SELECT @@@@@@@@@").IsOk());
+ }
+
+ Y_UNIT_TEST(StringMultiLineLiteralConsequitiveAt) {
+ UNIT_ASSERT(!SqlToYql("SELECT @").IsOk());
+ UNIT_ASSERT(!SqlToYql("SELECT @@").IsOk());
+ UNIT_ASSERT(!SqlToYql("SELECT @@@").IsOk());
+ UNIT_ASSERT( SqlToYql("SELECT @@@@").IsOk());
+ UNIT_ASSERT( SqlToYql("SELECT @@@@@").IsOk());
+
+ UNIT_ASSERT(!SqlToYql("SELECT @@@@@@").IsOk());
+ UNIT_ASSERT(!SqlToYql("SELECT @@@@@@@").IsOk());
+
+ UNIT_ASSERT( SqlToYql("SELECT @@@@@@@@").IsOk());
+ UNIT_ASSERT( SqlToYql("SELECT @@@@@@@@@").IsOk());
+ UNIT_ASSERT(!SqlToYql("SELECT @@@@@@@@@@").IsOk());
+ }
+
+ Y_UNIT_TEST(ConstnessForListDictSetCreate) {
+ auto req = "$foo = ($x, $y) -> (\"aaaa\");\n"
+ "\n"
+ "select\n"
+ " $foo(sum(key), ListCreate(String)),\n"
+ " $foo(sum(key), DictCreate(String, String)),\n"
+ " $foo(sum(key), SetCreate(String)),\n"
+ "from (select 1 as key);";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(CanUseEmptyTupleInWindowPartitionBy) {
+ auto req = "select sum(key) over w\n"
+ "from plato.Input\n"
+ "window w as (partition compact by (), (subkey), (), value || value as dvalue);";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(DenyAnsiOrderByLimitLegacyMode) {
+ auto req = "pragma DisableAnsiOrderByLimitInUnionAll;\n"
+ "use plato;\n"
+ "\n"
+ "select * from Input order by key limit 10\n"
+ "union all\n"
+ "select * from Input order by key limit 1;";
+
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: DisableAnsiOrderByLimitInUnionAll pragma is deprecated and no longer supported\n");
+ }
+
+ Y_UNIT_TEST(ReduceUsingUdfWithShortcutsWorks) {
+ auto req = "use plato;\n"
+ "\n"
+ "$arg = 'foo';\n"
+ "$func = XXX::YYY($arg);\n"
+ "\n"
+ "REDUCE Input ON key using $func(subkey);\n"
+ "REDUCE Input ON key using $func(UUU::VVV(TableRow()));\n";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ req = "use plato;\n"
+ "\n"
+ "$arg = 'foo';\n"
+ "$func = XXX::YYY($arg);\n"
+ "\n"
+ "REDUCE Input ON key using all $func(subkey);\n"
+ "REDUCE Input ON key using all $func(UUU::VVV(TableRow()));";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(YsonDisableStrict) {
+ UNIT_ASSERT(SqlToYql("pragma yson.DisableStrict = \"false\";").IsOk());
+ UNIT_ASSERT(SqlToYql("pragma yson.DisableStrict;").IsOk());
+ }
+
+ Y_UNIT_TEST(YsonStrict) {
+ UNIT_ASSERT(SqlToYql("pragma yson.Strict = \"false\";").IsOk());
+ UNIT_ASSERT(SqlToYql("pragma yson.Strict;").IsOk());
+ }
+
+ Y_UNIT_TEST(JoinByTuple) {
+ auto req = "use plato;\n"
+ "\n"
+ "select * from T1 as a\n"
+ "join T2 as b\n"
+ "on AsTuple(a.key, a.subkey) = AsTuple(b.key, b.subkey);";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(JoinByStruct) {
+ auto req = "use plato;\n"
+ "\n"
+ "select * from T1 as a\n"
+ "join T2 as b\n"
+ "on AsStruct(a.key as k, a.subkey as sk) = AsStruct(b.key as k, b.subkey as sk);";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(JoinByUdf) {
+ auto req = "use plato;\n"
+ "\n"
+ "select a.align\n"
+ "from T1 as a\n"
+ "join T2 as b\n"
+ "on Yson::SerializeJsonEncodeUtf8(a.align)=b.align;";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(EscapedIdentifierAsLambdaArg) {
+ auto req = "$f = ($`foo bar`, $x) -> { return $`foo bar` + $x; };\n"
+ "\n"
+ "select $f(1, 2);";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ const auto programm = GetPrettyPrint(res);
+ auto expected = "(lambda '(\"$foo bar\" \"$x\")";
+ UNIT_ASSERT(programm.find(expected) != TString::npos);
+ }
+
+ Y_UNIT_TEST(UdfSyntaxSugarOnlyCallable) {
+ auto req = "SELECT Udf(DateTime::FromString)('2022-01-01');";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ const auto programm = GetPrettyPrint(res);
+ auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType)))";
+ UNIT_ASSERT(programm.find(expected) != TString::npos);
+ }
+
+ Y_UNIT_TEST(UdfSyntaxSugarTypeNoRun) {
+ auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, 'foo' as TypeConfig)('2022-01-01');";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ const auto programm = GetPrettyPrint(res);
+ auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\")";
+ UNIT_ASSERT(programm.find(expected) != TString::npos);
+ }
+
+ Y_UNIT_TEST(UdfSyntaxSugarRunNoType) {
+ auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, Void() as RunConfig)('2022-01-01');";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ const auto programm = GetPrettyPrint(res);
+ auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"\" (Void))";
+ UNIT_ASSERT(programm.find(expected) != TString::npos);
+ }
+
+ Y_UNIT_TEST(UdfSyntaxSugarFullTest) {
+ auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, 'foo' as TypeConfig, Void() As RunConfig)('2022-01-01');";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ const auto programm = GetPrettyPrint(res);
+ auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\" (Void))";
+ UNIT_ASSERT(programm.find(expected) != TString::npos);
+ }
+
+ Y_UNIT_TEST(UdfSyntaxSugarOtherRunConfigs) {
+ auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, 'foo' as TypeConfig, '55' As RunConfig)('2022-01-01');";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ const auto programm = GetPrettyPrint(res);
+ auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\" (String '\"55\"))";
+ UNIT_ASSERT(programm.find(expected) != TString::npos);
+ }
+
+ Y_UNIT_TEST(UdfSyntaxSugarOtherRunConfigs2) {
+ auto req = "SELECT Udf(DateTime::FromString, String, Tuple<Int32, Float>, 'foo' as TypeConfig, AsTuple(32, 'no', AsStruct(1e-9 As SomeFloat)) As RunConfig)('2022-01-01');";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ const auto programm = GetPrettyPrint(res);
+ auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (DataType 'String) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\" '((Int32 '\"32\") (String '\"no\") (AsStruct '('\"SomeFloat\" (Double '\"1e-9\")))))";
+ UNIT_ASSERT(programm.find(expected) != TString::npos);
+ }
+
+ Y_UNIT_TEST(UdfSyntaxSugarOptional) {
+ auto req = "SELECT Udf(DateTime::FromString, String?, Int32??, Tuple<Int32, Float>, \"foo\" as TypeConfig, Void() As RunConfig)(\"2022-01-01\");";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ const auto programm = GetPrettyPrint(res);
+ auto expected = "(SqlCall '\"DateTime.FromString\" '((PositionalArgs (String '\"2022-01-01\")) (AsStruct)) (TupleType (TypeOf '((String '\"2022-01-01\"))) (TypeOf (AsStruct)) (TupleType (OptionalType (DataType 'String)) (OptionalType (OptionalType (DataType 'Int32))) (TupleType (DataType 'Int32) (DataType 'Float)))) '\"foo\" (Void))";
+ UNIT_ASSERT(programm.find(expected) != TString::npos);
+ }
+
+ Y_UNIT_TEST(CompactionPolicyParseCorrect) {
+ NYql::TAstParseResult res = SqlToYql(
+ R"( USE plato;
+ CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
+ WITH ( COMPACTION_POLICY = "SomeCompactionPreset" );)"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("compactionPolicy"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("SomeCompactionPreset"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(AutoPartitioningBySizeParseCorrect) {
+ NYql::TAstParseResult res = SqlToYql(
+ R"( USE plato;
+ CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
+ WITH ( AUTO_PARTITIONING_BY_SIZE = ENABLED );)"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("autoPartitioningBySize"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("ENABLED"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(UniformPartitionsParseCorrect) {
+ NYql::TAstParseResult res = SqlToYql(
+ R"( USE plato;
+ CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
+ WITH ( UNIFORM_PARTITIONS = 16 );)"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("uniformPartitions"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("16"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DateTimeTtlParseCorrect) {
+ NYql::TAstParseResult res = SqlToYql(
+ R"( USE plato;
+ CREATE TABLE tableName (Key Uint32, CreatedAt Timestamp, PRIMARY KEY (Key))
+ WITH (TTL = Interval("P1D") On CreatedAt);)"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("setTtlSettings"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiers"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("evictionDelay"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("86400000"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(IntTtlParseCorrect) {
+ NYql::TAstParseResult res = SqlToYql(
+ R"( USE plato;
+ CREATE TABLE tableName (Key Uint32, CreatedAt Uint32, PRIMARY KEY (Key))
+ WITH (TTL = Interval("P1D") On CreatedAt AS SECONDS);)"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("setTtlSettings"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiers"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("evictionDelay"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("86400000"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("columnUnit"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("seconds"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(TtlTieringParseCorrect) {
+ NYql::TAstParseResult res = SqlToYql(
+ R"( USE plato;
+ CREATE TABLE tableName (Key Uint32, CreatedAt Uint32, PRIMARY KEY (Key))
+ WITH (TTL =
+ Interval("P1D") TO EXTERNAL DATA SOURCE Tier1,
+ Interval("P2D") TO EXTERNAL DATA SOURCE Tier2,
+ Interval("P30D") DELETE
+ ON CreatedAt AS SECONDS);)"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("setTtlSettings"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiers"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("evictionDelay"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("storageName"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Tier1"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Tier2"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("86400000"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("172800000"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("2592000000"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("columnUnit"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("seconds"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(TtlTieringWithOtherActionsParseCorrect) {
+ NYql::TAstParseResult res = SqlToYql(
+ R"( USE plato;
+ ALTER TABLE tableName
+ ADD FAMILY cold (DATA = "rot"),
+ SET TTL
+ Interval("P1D") TO EXTERNAL DATA SOURCE Tier1,
+ Interval("P2D") TO EXTERNAL DATA SOURCE Tier2,
+ Interval("P30D") DELETE
+ ON CreatedAt,
+ ALTER COLUMN payload_v2 SET FAMILY cold,
+ ALTER FAMILY default SET DATA "ssd"
+ ;)"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("addColumnFamilies"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("cold"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alterColumnFamilies"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("default"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("setTtlSettings"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiers"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("evictionDelay"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("storageName"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Tier1"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("Tier2"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("86400000"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("172800000"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("2592000000"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(TieringParseCorrect) {
+ NYql::TAstParseResult res = SqlToYql(
+ R"( USE plato;
+ CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
+ WITH ( TIERING = 'my_tiering' );)"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tiering"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("my_tiering"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(StoreExternalBlobsParseCorrect) {
+ NYql::TAstParseResult res = SqlToYql(
+ R"( USE plato;
+ CREATE TABLE tableName (Key Uint32, Value String, PRIMARY KEY (Key))
+ WITH ( STORE_EXTERNAL_BLOBS = ENABLED );)"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("storeExternalBlobs"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("ENABLED"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DefaultValueColumn2) {
+ auto res = SqlToYql(R"( use plato;
+ $lambda = () -> {
+ RETURN CAST(RandomUuid(2) as String)
+ };
+
+ CREATE TABLE tableName (
+ Key Uint32 DEFAULT RandomNumber(1),
+ Value String DEFAULT $lambda,
+ PRIMARY KEY (Key)
+ );
+ )");
+
+ UNIT_ASSERT_C(res.Root, Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("RandomNumber"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("RandomUuid"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("columnConstrains"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("columnConstrains"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, program.find("Write"));
+
+#if 0
+ Cerr << program << Endl;
+#endif
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DefaultValueColumn3) {
+ auto res = SqlToYql(R"( use plato;
+
+ CREATE TABLE tableName (
+ database_id Utf8,
+ cloud_id Utf8,
+ global_id Utf8 DEFAULT database_id || "=====",
+ PRIMARY KEY (database_id)
+ );
+ )");
+
+ UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:6:40: Error: Column reference \"database_id\" is not allowed in current scope\n");
+ UNIT_ASSERT(!res.Root);
+ }
+
+ Y_UNIT_TEST(DefaultValueColumn) {
+ auto res = SqlToYql(R"( use plato;
+ CREATE TABLE tableName (
+ Key Uint32 FAMILY cold DEFAULT 5,
+ Value String FAMILY default DEFAULT "empty",
+ PRIMARY KEY (Key),
+ FAMILY default (
+ DATA = "test",
+ COMPRESSION = "lz4"
+ ),
+ FAMILY cold (
+ DATA = "test",
+ COMPRESSION = "off"
+ )
+ );
+ )");
+
+ UNIT_ASSERT_C(res.Root, Err2Str(res));
+
+#if 0
+ const auto program = GetPrettyPrint(res);
+ Cerr << program << Endl;
+#endif
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("default"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("columnConstrains"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("columnFamilies"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(ChangefeedParseCorrect) {
+ auto res = SqlToYql(R"( USE plato;
+ CREATE TABLE tableName (
+ Key Uint32, PRIMARY KEY (Key),
+ CHANGEFEED feedName WITH (
+ MODE = 'KEYS_ONLY',
+ FORMAT = 'json',
+ INITIAL_SCAN = TRUE,
+ VIRTUAL_TIMESTAMPS = FALSE,
+ BARRIERS_INTERVAL = Interval("PT1S"),
+ RETENTION_PERIOD = Interval("P1D"),
+ TOPIC_MIN_ACTIVE_PARTITIONS = 10,
+ AWS_REGION = 'aws:region'
+ )
+ );
+ )");
+ UNIT_ASSERT_C(res.Root, Err2Str(res));
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("changefeed"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("mode"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("KEYS_ONLY"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("format"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("json"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("initial_scan"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("true"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("virtual_timestamps"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("false"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("barriers_interval"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("retention_period"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("topic_min_active_partitions"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("aws_region"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("aws:region"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CloneForAsTableWorksWithCube) {
+ UNIT_ASSERT(SqlToYql("SELECT * FROM AS_TABLE([<|k1:1, k2:1|>]) GROUP BY CUBE(k1, k2);").IsOk());
+ }
+
+ Y_UNIT_TEST(WindowPartitionByColumnProperlyEscaped) {
+ NYql::TAstParseResult res = SqlToYql("SELECT SUM(key) OVER w FROM plato.Input WINDOW w AS (PARTITION BY `column with space`);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "CalcOverWindow") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("\"column with space\""));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("CalcOverWindow"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["CalcOverWindow"]);
+ }
+
+ Y_UNIT_TEST(WindowPartitionByExpressionWithoutAliasesAreAllowed) {
+ NYql::TAstParseResult res = SqlToYql("SELECT SUM(key) OVER w FROM plato.Input as i WINDOW w AS (PARTITION BY ii.subkey);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "AddMember") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("AddMember row 'group_w_0 (SqlAccess 'struct (Member row '\"ii\")"));
+ }
+ if (word == "CalcOverWindow") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("CalcOverWindow core '('\"group_w_0\")"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("CalcOverWindow"), 0}, {TString("AddMember"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["CalcOverWindow"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AddMember"]);
+ }
+
+ Y_UNIT_TEST(PqReadByAfterUse) {
+ ExpectFailWithError("use plato; pragma PqReadBy='plato2';",
+ "<main>:1:28: Error: Cluster in PqReadPqBy pragma differs from cluster specified in USE statement: plato2 != plato\n");
+
+ UNIT_ASSERT(SqlToYql("pragma PqReadBy='plato2';").IsOk());
+ UNIT_ASSERT(SqlToYql("pragma PqReadBy='plato2'; use plato;").IsOk());
+ UNIT_ASSERT(SqlToYql("$x='plato'; use rtmr:$x; pragma PqReadBy='plato2';").IsOk());
+ UNIT_ASSERT(SqlToYql("use plato; pragma PqReadBy='dq';").IsOk());
+ }
+
+ Y_UNIT_TEST(MrObject) {
+ NYql::TAstParseResult res = SqlToYql(
+ "declare $path as String;\n"
+ "select * from plato.object($path, `format`, \"comp\" || \"ression\" as compression, 1 as bar) with schema (Int32 as y, String as x)"
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "MrObject") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((MrObject (EvaluateAtom "$path") '"format" '('('"compression" (Concat (String '"comp") (String '"ression"))) '('"bar" (Int32 '"1")))))__"));
+ } else if (word == "userschema") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__('('('"userschema" (StructType '('"y" (DataType 'Int32)) '('"x" (DataType 'String))) '('"y" '"x"))))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("MrObject"), 0}, {TString("userschema"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["MrObject"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["userschema"]);
+ }
+
+ Y_UNIT_TEST(TableBindings) {
+ NSQLTranslation::TTranslationSettings settings = GetSettingsWithS3Binding("foo");
+ NYql::TAstParseResult res = SqlToYqlWithSettings(
+ "select * from bindings.foo",
+ settings
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "MrObject") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((MrTableConcat (Key '('table (String '"path")))) (Void) '('('"bar" '"1") '('"compression" '"ccompression") '('"format" '"format") '('"partitionedby" '"key" '"subkey") '('"userschema" (SqlTypeFromYson)__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("MrTableConcat"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["MrTableConcat"]);
+
+ settings.DefaultCluster = "plato";
+ settings.BindingsMode = NSQLTranslation::EBindingsMode::DISABLED;
+ res = SqlToYqlWithSettings(
+ "select * from bindings.foo",
+ settings
+ );
+ UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:1:15: Error: Please remove 'bindings.' from your query, the support for this syntax has ended, code: 4601\n");
+ UNIT_ASSERT(!res.Root);
+
+ settings.BindingsMode = NSQLTranslation::EBindingsMode::DROP;
+ res = SqlToYqlWithSettings(
+ "select * from bindings.foo",
+ settings
+ );
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine2 = [](const TString& word, const TString& line) {
+ if (word == "MrTableConcat") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((MrTableConcat (Key '('table (String '"foo")))) (Void) '())))__"));
+ }
+ };
+
+ TWordCountHive elementStat2 = {{TString("MrTableConcat"), 0}};
+ VerifyProgram(res, elementStat2, verifyLine2);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat2["MrTableConcat"]);
+
+ settings.BindingsMode = NSQLTranslation::EBindingsMode::DROP_WITH_WARNING;
+ res = SqlToYqlWithSettings(
+ "select * from bindings.foo",
+ settings
+ );
+ UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:1:15: Warning: Please remove 'bindings.' from your query, the support for this syntax will be dropped soon, code: 4538\n");
+ UNIT_ASSERT(res.Root);
+
+ TWordCountHive elementStat3 = {{TString("MrTableConcat"), 0}};
+ VerifyProgram(res, elementStat3, verifyLine2);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat3["MrTableConcat"]);
+ }
+
+ Y_UNIT_TEST(TableBindingsWithInsert) {
+ NSQLTranslation::TTranslationSettings settings = GetSettingsWithS3Binding("foo");
+ NYql::TAstParseResult res = SqlToYqlWithSettings(
+ "insert into bindings.foo with truncate (x, y) values (1, 2);",
+ settings
+ );
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((Write! world sink (Key '('table (String '"path"))) values '('('"bar" '"1") '('"compression" '"ccompression") '('"format" '"format") '('"partitionedby" '"key" '"subkey") '('"userschema" (SqlTypeFromYson)__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Write!"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]);
+
+ settings.DefaultCluster = "plato";
+ settings.BindingsMode = NSQLTranslation::EBindingsMode::DISABLED;
+ res = SqlToYqlWithSettings(
+ "insert into bindings.foo with truncate (x, y) values (1, 2);",
+ settings
+ );
+ UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:1:13: Error: Please remove 'bindings.' from your query, the support for this syntax has ended, code: 4601\n");
+ UNIT_ASSERT(!res.Root);
+
+ settings.BindingsMode = NSQLTranslation::EBindingsMode::DROP;
+ res = SqlToYqlWithSettings(
+ "insert into bindings.foo with truncate (x, y) values (1, 2);",
+ settings
+ );
+ UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine2 = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ //UNIT_ASSERT_VALUES_EQUAL(line, "");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__((Write! world sink (Key '('table (String '"foo"))) values '('('mode 'renew)))__"));
+ }
+ };
+
+ TWordCountHive elementStat2 = {{TString("Write!"), 0}};
+ VerifyProgram(res, elementStat2, verifyLine2);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat2["Write!"]);
+
+ settings.BindingsMode = NSQLTranslation::EBindingsMode::DROP_WITH_WARNING;
+ res = SqlToYqlWithSettings(
+ "insert into bindings.foo with truncate (x, y) values (1, 2);",
+ settings
+ );
+ UNIT_ASSERT_VALUES_EQUAL(Err2Str(res), "<main>:1:13: Warning: Please remove 'bindings.' from your query, the support for this syntax will be dropped soon, code: 4538\n");
+ UNIT_ASSERT(res.Root);
+
+ TWordCountHive elementStat3 = {{TString("Write!"), 0}};
+ VerifyProgram(res, elementStat3, verifyLine2);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat3["Write!"]);
+ }
+
+ Y_UNIT_TEST(TrailingCommaInWithout) {
+ UNIT_ASSERT(SqlToYql("SELECT * WITHOUT stream, FROM plato.Input").IsOk());
+ UNIT_ASSERT(SqlToYql("SELECT a.* WITHOUT a.intersect, FROM plato.Input AS a").IsOk());
+ UNIT_ASSERT(SqlToYql("SELECT a.* WITHOUT col1, col2, a.col3, FROM plato.Input AS a").IsOk());
+ }
+
+ Y_UNIT_TEST(NoStackOverflowOnBigCaseStatement) {
+ TStringBuilder req;
+ req << "select case 1 + 123";
+ for (size_t i = 0; i < 20000; ++i) {
+ req << " when " << i << " then " << i + 1;
+ }
+ req << " else 100500 end;";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(CollectPreaggregatedInListLiteral) {
+ UNIT_ASSERT(SqlToYql("SELECT [COUNT(DISTINCT a+b)] FROM plato.Input").IsOk());
+ }
+
+ Y_UNIT_TEST(SmartParenInGroupByClause) {
+ UNIT_ASSERT(SqlToYql("SELECT * FROM plato.Input GROUP BY (k, v)").IsOk());
+ }
+
+ Y_UNIT_TEST(AlterTableRenameToIsCorrect) {
+ UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table RENAME TO moved").IsOk());
+ }
+
+ Y_UNIT_TEST(AlterTableAddDropColumnIsCorrect) {
+ UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table ADD COLUMN addc uint64, DROP COLUMN dropc, ADD addagain uint64").IsOk());
+ }
+
+ Y_UNIT_TEST(AlterTableSetTTLIsCorrect) {
+ UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table SET (TTL = Interval(\"PT3H\") ON column)").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table SET (TTL = Interval(\"PT3H\") ON column AS SECONDS)").IsOk());
+ }
+
+ Y_UNIT_TEST(AlterTableSetTieringIsCorrect) {
+ UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table SET (TIERING = 'my_tiering')").IsOk());
+ }
+
+ Y_UNIT_TEST(AlterTableAddChangefeedIsCorrect) {
+ UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table ADD CHANGEFEED feed WITH (MODE = 'UPDATES', FORMAT = 'json')").IsOk());
+ }
+
+ Y_UNIT_TEST(AlterTableAlterChangefeedIsCorrect) {
+ UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table ALTER CHANGEFEED feed DISABLE").IsOk());
+ ExpectFailWithError("USE plato; ALTER TABLE table ALTER CHANGEFEED feed SET (FORMAT = 'proto');",
+ "<main>:1:57: Error: FORMAT alter is not supported\n");
+ }
+
+ Y_UNIT_TEST(AlterTableDropChangefeedIsCorrect) {
+ UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table DROP CHANGEFEED feed").IsOk());
+ }
+
+ Y_UNIT_TEST(AlterTableSetPartitioningIsCorrect) {
+ UNIT_ASSERT(SqlToYql("USE plato; ALTER TABLE table SET (AUTO_PARTITIONING_BY_SIZE = DISABLED)").IsOk());
+ }
+
+ Y_UNIT_TEST(AlterTableAddIndexWithIsNotSupported) {
+#if ANTLR_VER == 3
+ ExpectFailWithFuzzyError("USE plato; ALTER TABLE table ADD INDEX idx GLOBAL ON (col) WITH (a=b)",
+ "<main>:1:40: Error: with: alternative is not implemented yet: \\d+:\\d+: global_index\\n");
+#else
+ ExpectFailWithError("USE plato; ALTER TABLE table ADD INDEX idx GLOBAL ON (col) WITH (a=b)",
+ "<main>:1:40: Error: with: alternative is not implemented yet: \n");
+#endif
+ }
+
+ Y_UNIT_TEST(AlterTableAddIndexLocalIsNotSupported) {
+#if ANTLR_VER == 3
+ ExpectFailWithFuzzyError("USE plato; ALTER TABLE table ADD INDEX idx LOCAL ON (col)",
+ "<main>:1:40: Error: local: alternative is not implemented yet: \\d+:\\d+: local_index\\n");
+#else
+ ExpectFailWithError("USE plato; ALTER TABLE table ADD INDEX idx LOCAL ON (col)",
+ "<main>:1:40: Error: local: alternative is not implemented yet: \n");
+#endif
+ }
+
+ Y_UNIT_TEST(CreateTableAddIndexVector) {
+ const auto result = SqlToYql(R"(USE plato;
+ CREATE TABLE table (
+ pk INT32 NOT NULL,
+ col String,
+ INDEX idx GLOBAL USING vector_kmeans_tree
+ ON (col) COVER (col)
+ WITH (distance=cosine, vector_type=float, vector_dimension=1024,),
+ PRIMARY KEY (pk))
+ )");
+ UNIT_ASSERT_C(result.IsOk(), result.Issues.ToString());
+ }
+
+ Y_UNIT_TEST(AlterTableAddIndexVector) {
+ const auto result = SqlToYql(R"(USE plato;
+ ALTER TABLE table ADD INDEX idx
+ GLOBAL USING vector_kmeans_tree
+ ON (col) COVER (col)
+ WITH (distance=cosine, vector_type="float", vector_dimension=1024)
+ )");
+ UNIT_ASSERT_C(result.IsOk(), result.Issues.ToString());
+ }
+
+ Y_UNIT_TEST(AlterTableAddIndexUnknownSubtype) {
+ ExpectFailWithError("USE plato; ALTER TABLE table ADD INDEX idx GLOBAL USING unknown ON (col)",
+ "<main>:1:57: Error: UNKNOWN index subtype is not supported\n");
+ }
+
+ Y_UNIT_TEST(AlterTableAddIndexMissedParameter) {
+ ExpectFailWithError(R"(USE plato;
+ ALTER TABLE table ADD INDEX idx
+ GLOBAL USING vector_kmeans_tree
+ ON (col)
+ WITH (distance=cosine, vector_type=float)
+ )",
+ "<main>:5:52: Error: vector_dimension should be set\n");
+ }
+
+ Y_UNIT_TEST(AlterTableAlterIndexSetPartitioningIsCorrect) {
+ const auto result = SqlToYql("USE plato; ALTER TABLE table ALTER INDEX index SET AUTO_PARTITIONING_MIN_PARTITIONS_COUNT 10");
+ UNIT_ASSERT_C(result.IsOk(), result.Issues.ToString());
+ }
+
+ Y_UNIT_TEST(AlterTableAlterIndexSetMultiplePartitioningSettings) {
+ const auto result = SqlToYql("USE plato; ALTER TABLE table ALTER INDEX index SET "
+ "(AUTO_PARTITIONING_BY_LOAD = ENABLED, AUTO_PARTITIONING_MIN_PARTITIONS_COUNT = 10)"
+ );
+ UNIT_ASSERT_C(result.IsOk(), result.Issues.ToString());
+ }
+
+ Y_UNIT_TEST(AlterTableAlterIndexResetPartitioningIsNotSupported) {
+ ExpectFailWithError("USE plato; ALTER TABLE table ALTER INDEX index RESET (AUTO_PARTITIONING_MIN_PARTITIONS_COUNT)",
+ "<main>:1:55: Error: AUTO_PARTITIONING_MIN_PARTITIONS_COUNT reset is not supported\n"
+ );
+ }
+
+ Y_UNIT_TEST(AlterTableAlterColumnDropNotNullAstCorrect) {
+ auto reqSetNull = SqlToYql(R"(
+ USE plato;
+ CREATE TABLE tableName (
+ id Uint32,
+ val Uint32 NOT NULL,
+ PRIMARY KEY (id)
+ );
+
+ COMMIT;
+ ALTER TABLE tableName ALTER COLUMN val DROP NOT NULL;
+ )");
+
+ UNIT_ASSERT(reqSetNull.IsOk());
+ UNIT_ASSERT(reqSetNull.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(
+ R"(let world (Write! world sink (Key '('tablescheme (String '"tableName"))) (Void) '('('mode 'alter) '('actions '('('alterColumns '('('"val" '('changeColumnConstraints '('('drop_not_null)))))))))))"
+ ));
+ };
+
+ TWordCountHive elementStat({TString("\'mode \'alter")});
+ VerifyProgram(reqSetNull, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["\'mode \'alter"]);
+ }
+
+ Y_UNIT_TEST(AlterSequence) {
+ UNIT_ASSERT(SqlToYql(R"(
+ USE plato;
+ ALTER SEQUENCE sequence START WITH 10 INCREMENT 2 RESTART WITH 5;
+ )").IsOk());
+ UNIT_ASSERT(SqlToYql(R"(
+ USE plato;
+ ALTER SEQUENCE sequence INCREMENT 2;
+ )").IsOk());
+ UNIT_ASSERT(SqlToYql(R"(
+ USE plato;
+ ALTER SEQUENCE sequence INCREMENT 2 START 1000;
+ )").IsOk());
+ UNIT_ASSERT(SqlToYql(R"(
+ USE plato;
+ ALTER SEQUENCE sequence RESTART START 1000;
+ )").IsOk());
+ UNIT_ASSERT(SqlToYql(R"(
+ USE plato;
+ ALTER SEQUENCE IF EXISTS sequence INCREMENT 1000 START 100 RESTART;
+ )").IsOk());
+ UNIT_ASSERT(SqlToYql(R"(
+ USE plato;
+ ALTER SEQUENCE IF EXISTS sequence RESTART 1000 START WITH 100 INCREMENT BY 7;
+ )").IsOk());
+ }
+
+ Y_UNIT_TEST(AlterSequenceIncorrect) {
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence START WITH 10 INCREMENT 2 RESTART WITH 5 RESTART;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:75: Error: Restart value defined more than once\n");
+ }
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence START WITH 10 INCREMENT 2 START 100 RESTART WITH 5;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:60: Error: Start value defined more than once\n");
+ }
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence INCREMENT BY 7 START WITH 10 INCREMENT 2 RESTART WITH 5 RESTART;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:62: Error: Increment defined more than once\n");
+ }
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 100 START WITH 10 INCREMENT 2 RESTART WITH 5;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:77: Error: Restart value defined more than once\n");
+ }
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1234234543563435151456 START WITH 10 INCREMENT 2;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Error: Failed to parse number from string: 1234234543563435151456, number limit overflow\n");
+ }
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1 START WITH 9223372036854775817 INCREMENT 4;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Start value: 9223372036854775817 cannot be greater than max value: 9223372036854775807\n");
+ }
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 9223372036854775827 START WITH 5 INCREMENT 4;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Restart value: 9223372036854775827 cannot be greater than max value: 9223372036854775807\n");
+ }
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1 START WITH 4 INCREMENT 0;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Increment must not be zero\n");
+ }
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 0 START WITH 4 INCREMENT 1;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Restart value: 0 cannot be less than min value: 1\n");
+ }
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1 START WITH 0 INCREMENT 1;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Start value: 0 cannot be less than min value: 1\n");
+ }
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence RESTART WITH 1 START WITH 1 INCREMENT 9223372036854775837;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Increment: 9223372036854775837 cannot be greater than max value: 9223372036854775807\n");
+ }
+ }
+
+ Y_UNIT_TEST(AlterSequenceCorrect) {
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE sequence START WITH 10 INCREMENT 2 RESTART WITH 5;");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("sequence"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter"));
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("alter_if_exists"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("start"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("increment"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("restart"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE IF EXISTS sequence INCREMENT 2 RESTART;");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("sequence"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter_if_exists"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("increment"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("restart"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ {
+ NYql::TAstParseResult res = SqlToYql("USE plato; ALTER SEQUENCE IF EXISTS sequence START 10 INCREMENT BY 2;");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("sequence"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter_if_exists"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("start"));
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("restart"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("increment"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+ }
+
+ Y_UNIT_TEST(ShowCreateTable) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+ SHOW CREATE TABLE user;
+ )");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Read") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("showCreateTable"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("Read"), 0}, {TString("showCreateTable"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Read"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["showCreateTable"]);
+ }
+
+ Y_UNIT_TEST(OptionalAliases) {
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT foo FROM (SELECT key foo FROM Input);").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT a.x FROM Input1 a JOIN Input2 b ON a.key = b.key;").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; SELECT a.x FROM (VALUES (1,2), (3,4)) a(x,key) JOIN Input b ON a.key = b.key;").IsOk());
+ }
+
+ Y_UNIT_TEST(TableNameConstness) {
+ UNIT_ASSERT(SqlToYql("USE plato; $path = 'foo'; SELECT TableName($path), count(*) FROM Input;").IsOk());
+ UNIT_ASSERT(SqlToYql("$path = 'foo'; SELECT TableName($path, 'yt'), count(*) FROM plato.Input;").IsOk());
+ ExpectFailWithError("USE plato; SELECT TableName(), count(*) FROM plato.Input;",
+ "<main>:1:19: Error: Expression has to be an aggregation function or key column, because aggregation is used elsewhere in this subquery\n");
+ }
+
+ Y_UNIT_TEST(UseShouldWorkAsColumnName) {
+ UNIT_ASSERT(SqlToYql("select use from (select 1 as use);").IsOk());
+ }
+
+ Y_UNIT_TEST(TrueFalseWorkAfterDollar) {
+ UNIT_ASSERT(SqlToYql("$ true = false; SELECT $ true or false;").IsOk());
+ UNIT_ASSERT(SqlToYql("$False = 0; SELECT $False;").IsOk());
+ }
+
+ Y_UNIT_TEST(WithSchemaEquals) {
+ UNIT_ASSERT(SqlToYql("select * from plato.T with schema Struct<a:Int32, b:String>;").IsOk());
+ UNIT_ASSERT(SqlToYql("select * from plato.T with columns = Struct<a:Int32, b:String>;").IsOk());
+ }
+
+ Y_UNIT_TEST(WithNonStructSchemaS3) {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.ClusterMapping["s3bucket"] = NYql::S3ProviderName;
+ UNIT_ASSERT(SqlToYqlWithSettings("select * from s3bucket.`foo` with schema (col1 Int32, String as col2, Int64 as col3);", settings).IsOk());
+ }
+
+ Y_UNIT_TEST(AllowNestedTuplesInGroupBy) {
+ NYql::TAstParseResult res = SqlToYql("select count(*) from plato.Input group by 1 + (x, y, z);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Aggregate core '('\"group0\")"));
+ };
+
+ TWordCountHive elementStat({"Aggregate"});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT(elementStat["Aggregate"] == 1);
+ }
+
+ Y_UNIT_TEST(AllowGroupByWithParens) {
+ NYql::TAstParseResult res = SqlToYql("select count(*) from plato.Input group by (x, y as alias1, z);");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Aggregate core '('\"x\" '\"alias1\" '\"z\")"));
+ };
+
+ TWordCountHive elementStat({"Aggregate"});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT(elementStat["Aggregate"] == 1);
+ }
+
+ Y_UNIT_TEST(CreateAsyncReplicationParseCorrect) {
+ auto req = R"(
+ USE plato;
+ CREATE ASYNC REPLICATION MyReplication
+ FOR table1 AS table2, table3 AS table4
+ WITH (
+ CONNECTION_STRING = "grpc://localhost:2135/?database=/MyDatabase",
+ ENDPOINT = "localhost:2135",
+ DATABASE = "/MyDatabase"
+ );
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("MyReplication"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("create"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table1"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table2"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table3"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("table4"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("connection_string"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("grpc://localhost:2135/?database=/MyDatabase"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("endpoint"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("localhost:2135"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("database"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("/MyDatabase"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateAsyncReplicationUnsupportedSettings) {
+ auto reqTpl = R"(
+ USE plato;
+ CREATE ASYNC REPLICATION MyReplication
+ FOR table1 AS table2, table3 AS table4
+ WITH (
+ %s = "%s"
+ )
+ )";
+
+ auto settings = THashMap<TString, TString>{
+ {"STATE", "DONE"},
+ {"FAILOVER_MODE", "FORCE"},
+ };
+
+ for (const auto& [k, v] : settings) {
+ auto req = Sprintf(reqTpl, k.c_str(), v.c_str());
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), Sprintf("<main>:6:%zu: Error: %s is not supported in CREATE\n", 20 + k.size(), k.c_str()));
+ }
+ }
+
+ Y_UNIT_TEST(AsyncReplicationInvalidCommitInterval) {
+ auto req = R"(
+ USE plato;
+ CREATE ASYNC REPLICATION MyReplication
+ FOR table1 AS table2, table3 AS table4
+ WITH (
+ COMMIT_INTERVAL = "FOO"
+ );
+ )";
+
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:6:35: Error: Literal of Interval type is expected for COMMIT_INTERVAL\n");
+ }
+
+ Y_UNIT_TEST(AlterAsyncReplicationParseCorrect) {
+ auto req = R"(
+ USE plato;
+ ALTER ASYNC REPLICATION MyReplication
+ SET (
+ STATE = "DONE",
+ FAILOVER_MODE = "FORCE"
+ );
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("MyReplication"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("state"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("DONE"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("failover_mode"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("FORCE"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(AlterAsyncReplicationSettings) {
+ auto reqTpl = R"(
+ USE plato;
+ ALTER ASYNC REPLICATION MyReplication
+ SET (
+ %s = "%s"
+ )
+ )";
+
+ auto settings = THashMap<TString, TString>{
+ {"connection_string", "grpc://localhost:2135/?database=/MyDatabase"},
+ {"endpoint", "localhost:2135"},
+ {"database", "/MyDatabase"},
+ {"token", "foo"},
+ {"token_secret_name", "foo_secret_name"},
+ {"user", "user"},
+ {"password", "bar"},
+ {"password_secret_name", "bar_secret_name"},
+ };
+
+ for (const auto& [k, v] : settings) {
+ auto req = Sprintf(reqTpl, k.c_str(), v.c_str());
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&k, &v](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("MyReplication"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("alter"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(k));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(v));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+ }
+
+ Y_UNIT_TEST(AlterAsyncReplicationUnsupportedSettings) {
+ {
+ auto req = R"(
+ USE plato;
+ ALTER ASYNC REPLICATION MyReplication SET (CONSISTENCY_LEVEL = "GLOBAL");
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:80: Error: CONSISTENCY_LEVEL is not supported in ALTER\n");
+ }
+ {
+ auto req = R"(
+ USE plato;
+ ALTER ASYNC REPLICATION MyReplication SET (COMMIT_INTERVAL = Interval("PT10S"));
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:87: Error: COMMIT_INTERVAL is not supported in ALTER\n");
+ }
+ }
+
+ Y_UNIT_TEST(AsyncReplicationInvalidSettings) {
+ auto req = R"(
+ USE plato;
+ ALTER ASYNC REPLICATION MyReplication SET (FOO = "BAR");
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:62: Error: Unknown replication setting: FOO\n");
+ }
+
+ Y_UNIT_TEST(DropAsyncReplicationParseCorrect) {
+ auto req = R"(
+ USE plato;
+ DROP ASYNC REPLICATION MyReplication;
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("MyReplication"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DropAsyncReplicationCascade) {
+ auto req = R"(
+ USE plato;
+ DROP ASYNC REPLICATION MyReplication CASCADE;
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropCascade"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(PragmaCompactGroupBy) {
+ auto req = "PRAGMA CompactGroupBy; SELECT key, COUNT(*) FROM plato.Input GROUP BY key;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Aggregate") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('compact)"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Aggregate"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Aggregate"]);
+ }
+
+ Y_UNIT_TEST(PragmaDisableCompactGroupBy) {
+ auto req = "PRAGMA DisableCompactGroupBy; SELECT key, COUNT(*) FROM plato.Input GROUP /*+ compact() */ BY key;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Aggregate") {
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'('compact)"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Aggregate"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Aggregate"]);
+ }
+
+ Y_UNIT_TEST(AutoSampleWorksWithNamedSubquery) {
+ UNIT_ASSERT(SqlToYql("$src = select * from plato.Input; select * from $src sample 0.2").IsOk());
+ }
+
+ Y_UNIT_TEST(AutoSampleWorksWithSubquery) {
+ UNIT_ASSERT(SqlToYql("select * from (select * from plato.Input) sample 0.2").IsOk());
+ }
+
+ Y_UNIT_TEST(CreateTableTrailingComma) {
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE tableName (Key Uint32, PRIMARY KEY (Key),);").IsOk());
+ UNIT_ASSERT(SqlToYql("USE plato; CREATE TABLE tableName (Key Uint32,);").IsOk());
+ }
+
+ Y_UNIT_TEST(BetweenSymmetric) {
+ UNIT_ASSERT(SqlToYql("select 3 between symmetric 5 and 4;").IsOk());
+ UNIT_ASSERT(SqlToYql("select 3 between asymmetric 5 and 4;").IsOk());
+ UNIT_ASSERT(SqlToYql("use plato; select key between symmetric and and and from Input;").IsOk());
+ UNIT_ASSERT(SqlToYql("use plato; select key between and and and from Input;").IsOk());
+ }
+}
+
+Y_UNIT_TEST_SUITE(ExternalFunction) {
+ Y_UNIT_TEST(ValidUseFunctions) {
+
+ UNIT_ASSERT(SqlToYql(
+ "PROCESS plato.Input"
+ " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo', <|a: 123, b: a + 641|>)"
+ " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,"
+ " CONCURRENCY=3, OPTIMIZE_FOR='CALLS'").IsOk());
+
+ // use CALLS without quotes, as keyword
+ UNIT_ASSERT(SqlToYql(
+ "PROCESS plato.Input"
+ " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo')"
+ " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,"
+ " OPTIMIZE_FOR=CALLS").IsOk());
+
+ UNIT_ASSERT(SqlToYql(
+ "PROCESS plato.Input"
+ " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo', TableRow())"
+ " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,"
+ " CONCURRENCY=3").IsOk());
+
+ UNIT_ASSERT(SqlToYql(
+ "PROCESS plato.Input"
+ " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo')"
+ " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,"
+ " CONCURRENCY=3, BATCH_SIZE=1000000, CONNECTION='yc-folder34fse-con',"
+ " INIT=[0, 900]").IsOk());
+
+ UNIT_ASSERT(SqlToYql(
+ "PROCESS plato.Input"
+ " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'bar', TableRow())"
+ " WITH UNKNOWN_PARAM_1='837747712', UNKNOWN_PARAM_2=Tuple<Uint16, Utf8>,"
+ " INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>").IsOk());
+ }
+
+
+ Y_UNIT_TEST(InValidUseFunctions) {
+ ExpectFailWithError("PROCESS plato.Input USING some::udf(*) WITH INPUT_TYPE=Struct<a:Int32>",
+ "<main>:1:33: Error: PROCESS without USING EXTERNAL FUNCTION doesn't allow WITH block\n");
+
+ ExpectFailWithError("PROCESS plato.Input USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'jhhjfh88134d')"
+ " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>"
+ " ASSUME ORDER BY key",
+ "<main>:1:129: Error: PROCESS with USING EXTERNAL FUNCTION doesn't allow ASSUME block\n");
+
+ ExpectFailWithError("PROCESS plato.Input USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo', 'bar', 'baz')",
+ "<main>:1:15: Error: EXTERNAL FUNCTION requires from 2 to 3 arguments, but got: 4\n");
+
+ ExpectFailWithError("PROCESS plato.Input\n"
+ " USING EXTERNAL FUNCTION('YANDEX-CLOUD', 'foo', <|field_1: a1, field_b: b1|>)\n"
+ " WITH INPUT_TYPE=Struct<a:Int32>, OUTPUT_TYPE=Struct<b:Int32>,\n"
+ " CONCURRENCY=3, BATCH_SIZE=1000000, CONNECTION='yc-folder34fse-con',\n"
+ " CONCURRENCY=5, INPUT_TYPE=Struct<b:Bool>,\n"
+ " INIT=[0, 900]\n",
+ "<main>:5:2: Error: WITH \"CONCURRENCY\" clause should be specified only once\n"
+ "<main>:5:17: Error: WITH \"INPUT_TYPE\" clause should be specified only once\n");
+ }
+}
+
+Y_UNIT_TEST_SUITE(SqlToYQLErrors) {
+ Y_UNIT_TEST(UdfSyntaxSugarMissingCall) {
+ auto req = "SELECT Udf(DateTime::FromString, \"foo\" as RunConfig);";
+ auto res = SqlToYql(req);
+ TString a1 = Err2Str(res);
+ TString a2("<main>:1:8: Error: Abstract Udf Node can't be used as a part of expression.\n");
+ UNIT_ASSERT_NO_DIFF(a1, a2);
+ }
+
+ Y_UNIT_TEST(UdfSyntaxSugarIsNotCallable) {
+ auto req = "SELECT Udf(123, \"foo\" as RunConfig);";
+ auto res = SqlToYql(req);
+ TString a1 = Err2Str(res);
+ TString a2("<main>:1:8: Error: Udf: first argument must be a callable, like Foo::Bar\n");
+ UNIT_ASSERT_NO_DIFF(a1, a2);
+ }
+
+ Y_UNIT_TEST(UdfSyntaxSugarNoArgs) {
+ auto req = "SELECT Udf()();";
+ auto res = SqlToYql(req);
+ TString a1 = Err2Str(res);
+ TString a2("<main>:1:8: Error: Udf: expected at least one argument\n");
+ UNIT_ASSERT_NO_DIFF(a1, a2);
+ }
+
+ Y_UNIT_TEST(StrayUTF8) {
+ /// 'c' in plato is russian here
+ NYql::TAstParseResult res = SqlToYql("select * from сedar.Input");
+ UNIT_ASSERT(!res.Root);
+
+ TString a1 = Err2Str(res);
+#if ANTLR_VER == 3
+ TString a2(R"foo(<main>:1:14: Error: Unexpected character 'с' (Unicode character <1089>) : cannot match to any predicted input...
+
+<main>:1:15: Error: Unexpected character : cannot match to any predicted input...
+
+)foo");
+#else
+ TString a2(R"foo(<main>:1:14: Error: token recognition error at: 'с'
+)foo");
+#endif
+ UNIT_ASSERT_NO_DIFF(a1, a2);
+ }
+
+ Y_UNIT_TEST(IvalidStringLiteralWithEscapedBackslash) {
+ NYql::TAstParseResult res1 = SqlToYql(R"foo($bar = 'a\\'b';)foo");
+ NYql::TAstParseResult res2 = SqlToYql(R"foo($bar = "a\\"b";)foo");
+ UNIT_ASSERT(!res1.Root);
+ UNIT_ASSERT(!res2.Root);
+
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res1), "<main>:1:15: Error: Unexpected character : syntax error...\n\n");
+ UNIT_ASSERT_NO_DIFF(Err2Str(res2), "<main>:1:15: Error: Unexpected character : syntax error...\n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res1), "<main>:1:13: Error: token recognition error at: '';'\n");
+ UNIT_ASSERT_NO_DIFF(Err2Str(res2), "<main>:1:13: Error: token recognition error at: '\";'\n");
+#endif
+ }
+
+ Y_UNIT_TEST(InvalidHexInStringLiteral) {
+ NYql::TAstParseResult res = SqlToYql("select \"foo\\x1\\xfe\"");
+ UNIT_ASSERT(!res.Root);
+ TString a1 = Err2Str(res);
+ TString a2 = "<main>:1:15: Error: Failed to parse string literal: Invalid hexadecimal value\n";
+
+ UNIT_ASSERT_NO_DIFF(a1, a2);
+ }
+
+ Y_UNIT_TEST(InvalidOctalInMultilineStringLiteral) {
+ NYql::TAstParseResult res = SqlToYql("select \"foo\n"
+ "bar\n"
+ "\\01\"");
+ UNIT_ASSERT(!res.Root);
+ TString a1 = Err2Str(res);
+ TString a2 = "<main>:3:4: Error: Failed to parse string literal: Invalid octal value\n";
+
+ UNIT_ASSERT_NO_DIFF(a1, a2);
+ }
+
+ Y_UNIT_TEST(InvalidDoubleAtString) {
+ NYql::TAstParseResult res = SqlToYql("select @@@@@@");
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:13: Error: Unexpected character : syntax error...\n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: extraneous input '@' expecting {<EOF>, ';'}\n");
+#endif
+ }
+
+ Y_UNIT_TEST(InvalidDoubleAtStringWhichWasAcceptedEarlier) {
+ NYql::TAstParseResult res = SqlToYql("SELECT @@foo@@ @ @@bar@@");
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:7: Error: Unexpected token '@@foo@@' : cannot match to any predicted input...\n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:15: Error: mismatched input '@' expecting {<EOF>, ';'}\n");
+#endif
+ }
+
+ Y_UNIT_TEST(InvalidStringFromTable) {
+ NYql::TAstParseResult res = SqlToYql("select \"FOO\"\"BAR from plato.foo");
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:31: Error: Unexpected character : syntax error...\n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: token recognition error at: '\"BAR from plato.foo'\n");
+#endif
+ }
+
+ Y_UNIT_TEST(InvalidDoubleAtStringFromTable) {
+ NYql::TAstParseResult res = SqlToYql("select @@@@@@ from plato.foo");
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:28: Error: Unexpected character : syntax error...\n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: mismatched input '@' expecting {<EOF>, ';'}\n");
+#endif
+ }
+
+ Y_UNIT_TEST(SelectInvalidSyntax) {
+ NYql::TAstParseResult res = SqlToYql("select 1 form Wat");
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:14: Error: Unexpected token 'Wat' : cannot match to any predicted input...\n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:14: Error: extraneous input 'Wat' expecting {<EOF>, ';'}\n");
+#endif
+ }
+
+ Y_UNIT_TEST(SelectNoCluster) {
+ NYql::TAstParseResult res = SqlToYql("select foo from bar");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: No cluster name given and no default cluster is selected\n");
+ }
+
+ Y_UNIT_TEST(SelectDuplicateColumns) {
+ NYql::TAstParseResult res = SqlToYql("select a, a from plato.Input");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:11: Error: Unable to use duplicate column names. Collision in name: a\n");
+ }
+
+ Y_UNIT_TEST(SelectDuplicateLabels) {
+ NYql::TAstParseResult res = SqlToYql("select a as foo, b as foo from plato.Input");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:18: Error: Unable to use duplicate column names. Collision in name: foo\n");
+ }
+
+ Y_UNIT_TEST(SelectCaseWithoutThen) {
+ NYql::TAstParseResult res = SqlToYql("select case when true 1;");
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res),
+ "<main>:1:22: Error: Unexpected token absence : Missing THEN \n\n"
+ "<main>:1:23: Error: Unexpected token absence : Missing END \n\n"
+ );
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res),
+ "<main>:1:22: Error: missing THEN at \'1\'\n"
+ "<main>:1:23: Error: extraneous input \';\' expecting {ELSE, END, WHEN}\n"
+ );
+#endif
+ }
+
+ Y_UNIT_TEST(SelectComplexCaseWithoutThen) {
+ NYql::TAstParseResult res = SqlToYql(
+ "SELECT *\n"
+ "FROM plato.Input AS a\n"
+ "WHERE CASE WHEN a.key = \"foo\" a.subkey ELSE a.value END\n"
+ );
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:30: Error: Unexpected token absence : Missing THEN \n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:30: Error: missing THEN at 'a'\n");
+#endif
+ }
+
+ Y_UNIT_TEST(SelectCaseWithoutEnd) {
+ NYql::TAstParseResult res = SqlToYql("select case a when b then c end from plato.Input");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: ELSE is required\n");
+ }
+
+ Y_UNIT_TEST(SelectWithBadAggregationNoInput) {
+ NYql::TAstParseResult res = SqlToYql("select a, Min(b), c");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res),
+ "<main>:1:1: Error: Column references are not allowed without FROM\n"
+ "<main>:1:8: Error: Column reference 'a'\n"
+ "<main>:1:1: Error: Column references are not allowed without FROM\n"
+ "<main>:1:15: Error: Column reference 'b'\n"
+ "<main>:1:1: Error: Column references are not allowed without FROM\n"
+ "<main>:1:19: Error: Column reference 'c'\n"
+ );
+ }
+
+ Y_UNIT_TEST(SelectWithBadAggregation) {
+ ExpectFailWithError("select count(*), 1 + key from plato.Input",
+ "<main>:1:22: Error: Column `key` must either be a key column in GROUP BY or it should be used in aggregation function\n");
+ }
+
+ Y_UNIT_TEST(SelectWithBadAggregatedTerms) {
+ ExpectFailWithError("select key, 2 * subkey from plato.Input group by key",
+ "<main>:1:17: Error: Column `subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
+ }
+
+ Y_UNIT_TEST(SelectDistinctWithBadAggregation) {
+ ExpectFailWithError("select distinct count(*), 1 + key from plato.Input",
+ "<main>:1:31: Error: Column `key` must either be a key column in GROUP BY or it should be used in aggregation function\n");
+ ExpectFailWithError("select distinct key, 2 * subkey from plato.Input group by key",
+ "<main>:1:26: Error: Column `subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
+ }
+
+ Y_UNIT_TEST(SelectWithBadAggregationInHaving) {
+ ExpectFailWithError("select key from plato.Input group by key\n"
+ "having \"f\" || value == \"foo\"",
+ "<main>:2:15: Error: Column `value` must either be a key column in GROUP BY or it should be used in aggregation function\n");
+ }
+
+ Y_UNIT_TEST(JoinWithNonAggregatedColumnInProjection) {
+ ExpectFailWithError("select a.key, 1 + b.subkey\n"
+ "from plato.Input1 as a join plato.Input2 as b using(key)\n"
+ "group by a.key;",
+ "<main>:1:19: Error: Column `b.subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
+
+ ExpectFailWithError("select a.key, 1 + b.subkey.x\n"
+ "from plato.Input1 as a join plato.Input2 as b using(key)\n"
+ "group by a.key;",
+ "<main>:1:19: Error: Column must either be a key column in GROUP BY or it should be used in aggregation function\n");
+ }
+
+ Y_UNIT_TEST(SelectWithBadAggregatedTermsWithSources) {
+ ExpectFailWithError("select key, 1 + a.subkey\n"
+ "from plato.Input1 as a\n"
+ "group by a.key;",
+ "<main>:1:17: Error: Column `a.subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
+ ExpectFailWithError("select key, 1 + a.subkey.x\n"
+ "from plato.Input1 as a\n"
+ "group by a.key;",
+ "<main>:1:17: Error: Column must either be a key column in GROUP BY or it should be used in aggregation function\n");
+ }
+
+ Y_UNIT_TEST(WarnForAggregationBySelectAlias) {
+ NYql::TAstParseResult res = SqlToYql("select c + 1 as c from plato.Input\n"
+ "group by c");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res),
+ "<main>:2:11: Warning: GROUP BY will aggregate by column `c` instead of aggregating by SELECT expression with same alias, code: 4532\n"
+ "<main>:1:10: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n");
+
+ res = SqlToYql("select c + 1 as c from plato.Input\n"
+ "group by Math::Floor(c + 2) as c;");
+
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res),
+ "<main>:2:22: Warning: GROUP BY will aggregate by column `c` instead of aggregating by SELECT expression with same alias, code: 4532\n"
+ "<main>:1:10: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n");
+ }
+
+ Y_UNIT_TEST(NoWarnForAggregationBySelectAliasWhenAggrFunctionsAreUsedInAlias) {
+ NYql::TAstParseResult res = SqlToYql("select\n"
+ " cast(avg(val) as int) as value,\n"
+ " value as key\n"
+ "from\n"
+ " plato.Input\n"
+ "group by value");
+
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT(res.Issues.Size() == 0);
+
+ res = SqlToYql("select\n"
+ " cast(avg(val) over w as int) as value,\n"
+ " value as key\n"
+ "from\n"
+ " plato.Input\n"
+ "group by value\n"
+ "window w as ()");
+
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT(res.Issues.Size() == 0);
+ }
+
+ Y_UNIT_TEST(NoWarnForAggregationBySelectAliasWhenQualifiedNameIsUsed) {
+ NYql::TAstParseResult res = SqlToYql("select\n"
+ " Unwrap(a.key) as key\n"
+ "from plato.Input as a\n"
+ "join plato.Input2 as b using(k)\n"
+ "group by a.key;");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT(res.Issues.Size() == 0);
+
+ res = SqlToYql("select Unwrap(a.key) as key\n"
+ "from plato.Input as a\n"
+ "group by a.key;");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT(res.Issues.Size() == 0);
+ }
+
+ Y_UNIT_TEST(NoWarnForAggregationBySelectAliasWhenTrivialRenamingIsUsed) {
+ NYql::TAstParseResult res = SqlToYql("select a.key as key\n"
+ "from plato.Input as a\n"
+ "group by key;");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT(res.Issues.Size() == 0);
+
+ res = SqlToYql("select key as key\n"
+ "from plato.Input\n"
+ "group by key;");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT(res.Issues.Size() == 0);
+ }
+
+ Y_UNIT_TEST(ErrorByAggregatingByExpressionWithSameExpressionInSelect) {
+ ExpectFailWithError("select k * 2 from plato.Input group by k * 2",
+ "<main>:1:8: Error: Column `k` must either be a key column in GROUP BY or it should be used in aggregation function\n");
+ }
+
+ Y_UNIT_TEST(ErrorForAggregationBySelectAlias) {
+ ExpectFailWithError("select key, Math::Floor(1.1 + a.subkey) as foo\n"
+ "from plato.Input as a\n"
+ "group by a.key, foo;",
+ "<main>:3:17: Warning: GROUP BY will aggregate by column `foo` instead of aggregating by SELECT expression with same alias, code: 4532\n"
+ "<main>:1:19: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n"
+ "<main>:1:31: Error: Column `a.subkey` must either be a key column in GROUP BY or it should be used in aggregation function\n");
+
+ ExpectFailWithError("select c + 1 as c from plato.Input\n"
+ "group by Math::Floor(c + 2);",
+ "<main>:2:22: Warning: GROUP BY will aggregate by column `c` instead of aggregating by SELECT expression with same alias, code: 4532\n"
+ "<main>:1:10: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n"
+ "<main>:1:8: Error: Column `c` must either be a key column in GROUP BY or it should be used in aggregation function\n");
+ }
+
+ Y_UNIT_TEST(SelectWithDuplicateGroupingColumns) {
+ NYql::TAstParseResult res = SqlToYql("select c from plato.Input group by c, c");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Duplicate grouping column: c\n");
+ }
+
+ Y_UNIT_TEST(SelectWithBadAggregationInGrouping) {
+ NYql::TAstParseResult res = SqlToYql("select a, Min(b), c group by c");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Column references are not allowed without FROM\n"
+ "<main>:1:30: Error: Column reference 'c'\n");
+ }
+
+ Y_UNIT_TEST(SelectWithOpOnBadAggregation) {
+ ExpectFailWithError("select 1 + a + Min(b) from plato.Input",
+ "<main>:1:12: Error: Column `a` must either be a key column in GROUP BY or it should be used in aggregation function\n");
+ }
+
+ Y_UNIT_TEST(SelectOrderByConstantNum) {
+ NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by 1");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:36: Error: Unable to ORDER BY constant expression\n");
+ }
+
+ Y_UNIT_TEST(SelectOrderByConstantExpr) {
+ NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by 1 * 42");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:38: Error: Unable to ORDER BY constant expression\n");
+ }
+
+ Y_UNIT_TEST(SelectOrderByConstantString) {
+ NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by \"nest\"");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:36: Error: Unable to ORDER BY constant expression\n");
+ }
+
+ Y_UNIT_TEST(SelectOrderByAggregated) {
+ NYql::TAstParseResult res = SqlToYql("select a from plato.Input order by min(a)");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:36: Error: Unable to ORDER BY aggregated values\n");
+ }
+
+ Y_UNIT_TEST(ErrorInOrderByExpresison) {
+ NYql::TAstParseResult res = SqlToYql("select key, value from plato.Input order by (key as zey)");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:45: Error: You should use in ORDER BY column name, qualified field, callable function or expression\n");
+ }
+
+ Y_UNIT_TEST(ErrorsInOrderByWhenColumnIsMissingInProjection) {
+ ExpectFailWithError("select subkey from (select 1 as subkey) order by key", "<main>:1:50: Error: Column key is not in source column set\n");
+ ExpectFailWithError("select subkey from plato.Input as a order by x.key", "<main>:1:46: Error: Unknown correlation name: x\n");
+ ExpectFailWithError("select distinct a, b from plato.Input order by c", "<main>:1:48: Error: Column c is not in source column set. Did you mean a?\n");
+ ExpectFailWithError("select count(*) as a from plato.Input order by c", "<main>:1:48: Error: Column c is not in source column set. Did you mean a?\n");
+ ExpectFailWithError("select count(*) as a, b, from plato.Input group by b order by c", "<main>:1:63: Error: Column c is not in source column set. Did you mean a?\n");
+ UNIT_ASSERT(SqlToYql("select a, b from plato.Input order by c").IsOk());
+ }
+
+ Y_UNIT_TEST(SelectAggregatedWhere) {
+ NYql::TAstParseResult res = SqlToYql("select * from plato.Input where count(key)");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:33: Error: Can not use aggregated values in filtering\n");
+ }
+
+ Y_UNIT_TEST(DoubleFrom) {
+ NYql::TAstParseResult res = SqlToYql("from plato.Input select * from plato.Input");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:27: Error: Only one FROM clause is allowed\n");
+ }
+
+ Y_UNIT_TEST(SelectJoinMissingCorrName) {
+ NYql::TAstParseResult res = SqlToYql("select * from plato.Input1 as a join plato.Input2 as b on a.key == key");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:65: Error: JOIN: column requires correlation name\n");
+ }
+
+ Y_UNIT_TEST(SelectJoinMissingCorrName1) {
+ NYql::TAstParseResult res = SqlToYql(
+ "use plato;\n"
+ "$foo = select * from Input1;\n"
+ "select * from Input2 join $foo USING(key);\n"
+ );
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:27: Error: JOIN: missing correlation name for source\n");
+ }
+
+ Y_UNIT_TEST(SelectJoinMissingCorrName2) {
+ NYql::TAstParseResult res = SqlToYql(
+ "use plato;\n"
+ "$foo = select * from Input1;\n"
+ "select * from Input2 cross join $foo;\n"
+ );
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:33: Error: JOIN: missing correlation name for source\n");
+ }
+
+ Y_UNIT_TEST(SelectJoinEmptyCorrNames) {
+ NYql::TAstParseResult res = SqlToYql(
+ "$left = (SELECT * FROM plato.Input1 LIMIT 2);\n"
+ "$right = (SELECT * FROM plato.Input2 LIMIT 2);\n"
+ "SELECT * FROM $left FULL JOIN $right USING (key);\n"
+ );
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:45: Error: At least one correlation name is required in join\n");
+ }
+
+ Y_UNIT_TEST(SelectJoinSameCorrNames) {
+ NYql::TAstParseResult res = SqlToYql("SELECT Input.key FROM plato.Input JOIN plato.Input1 ON Input.key == Input.subkey\n");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:66: Error: JOIN: different correlation names are required for joined tables\n");
+ }
+
+ Y_UNIT_TEST(SelectJoinConstPredicateArg) {
+ NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input1 as A JOIN plato.Input2 as B ON A.key == B.key AND A.subkey == \"wtf\"\n");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:87: Error: JOIN: each equality predicate argument must depend on exactly one JOIN input\n");
+ }
+
+ Y_UNIT_TEST(SelectJoinNonEqualityPredicate) {
+ NYql::TAstParseResult res = SqlToYql("SELECT * FROM plato.Input1 as A JOIN plato.Input2 as B ON A.key == B.key AND A.subkey > B.subkey\n");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:87: Error: JOIN ON expression must be a conjunction of equality predicates\n");
+ }
+
+ Y_UNIT_TEST(SelectEquiJoinCorrNameOutOfScope) {
+ NYql::TAstParseResult res = SqlToYql(
+ "PRAGMA equijoin;\n"
+ "SELECT * FROM plato.A JOIN plato.B ON A.key == C.key JOIN plato.C ON A.subkey == C.subkey;\n"
+ );
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:45: Error: JOIN: can not use source: C in equality predicate, it is out of current join scope\n");
+ }
+
+ Y_UNIT_TEST(SelectEquiJoinNoRightSource) {
+ NYql::TAstParseResult res = SqlToYql(
+ "PRAGMA equijoin;\n"
+ "SELECT * FROM plato.A JOIN plato.B ON A.key == B.key JOIN plato.C ON A.subkey == B.subkey;\n"
+ );
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:79: Error: JOIN ON equality predicate must have one of its arguments from the rightmost source\n");
+ }
+
+ Y_UNIT_TEST(SelectEquiJoinOuterWithoutType) {
+ NYql::TAstParseResult res = SqlToYql(
+ "SELECT * FROM plato.A Outer JOIN plato.B ON A.key == B.key;\n"
+ );
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:23: Error: Invalid join type: OUTER JOIN. OUTER keyword is optional and can only be used after LEFT, RIGHT or FULL\n");
+ }
+
+ Y_UNIT_TEST(SelectEquiJoinOuterWithWrongType) {
+ NYql::TAstParseResult res = SqlToYql(
+ "SELECT * FROM plato.A LEFT semi OUTER JOIN plato.B ON A.key == B.key;\n"
+ );
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:33: Error: Invalid join type: LEFT SEMI OUTER JOIN. OUTER keyword is optional and can only be used after LEFT, RIGHT or FULL\n");
+ }
+
+ Y_UNIT_TEST(InsertNoCluster) {
+ NYql::TAstParseResult res = SqlToYql("insert into Output (foo) values (1)");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: No cluster name given and no default cluster is selected\n");
+ }
+
+ Y_UNIT_TEST(InsertValuesNoLabels) {
+ NYql::TAstParseResult res = SqlToYql("insert into plato.Output values (1)");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: INSERT INTO ... VALUES requires specification of table columns\n");
+ }
+
+ Y_UNIT_TEST(UpsertValuesNoLabelsKikimr) {
+ NYql::TAstParseResult res = SqlToYql("upsert into plato.Output values (1)", 10, TString(NYql::KikimrProviderName));
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: UPSERT INTO ... VALUES requires specification of table columns\n");
+ }
+
+ Y_UNIT_TEST(ReplaceValuesNoLabelsKikimr) {
+ NYql::TAstParseResult res = SqlToYql("replace into plato.Output values (1)", 10, TString(NYql::KikimrProviderName));
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:20: Error: REPLACE INTO ... VALUES requires specification of table columns\n");
+ }
+
+ Y_UNIT_TEST(InsertValuesInvalidLabels) {
+ NYql::TAstParseResult res = SqlToYql("insert into plato.Output (foo) values (1, 2)");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:27: Error: VALUES have 2 columns, INSERT INTO expects: 1\n");
+ }
+
+ Y_UNIT_TEST(BuiltinFileOpNoArgs) {
+ NYql::TAstParseResult res = SqlToYql("select FilePath()");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: FilePath() requires exactly 1 arguments, given: 0\n");
+ }
+
+ Y_UNIT_TEST(ProcessWithHaving) {
+ NYql::TAstParseResult res = SqlToYql("process plato.Input using some::udf(value) having value == 1");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:15: Error: PROCESS does not allow HAVING yet! You may request it on yql@ maillist.\n");
+ }
+
+ Y_UNIT_TEST(ReduceNoBy) {
+ NYql::TAstParseResult res = SqlToYql("reduce plato.Input using some::udf(value)");
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: Unexpected token absence : Missing ON \n\n<main>:1:25: Error: Unexpected token absence : Missing USING \n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: mismatched input 'using' expecting {',', ON, PRESORT}\n");
+#endif
+ }
+
+ Y_UNIT_TEST(ReduceDistinct) {
+ NYql::TAstParseResult res = SqlToYql("reduce plato.Input on key using some::udf(distinct value)");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:43: Error: DISTINCT can not be used in PROCESS/REDUCE\n");
+ }
+
+ Y_UNIT_TEST(CreateTableWithView) {
+ NYql::TAstParseResult res = SqlToYql("CREATE TABLE plato.foo:bar (key INT);");
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:22: Error: Unexpected token ':' : syntax error...\n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:22: Error: mismatched input ':' expecting '('\n");
+#endif
+ }
+
+ Y_UNIT_TEST(AsteriskWithSomethingAfter) {
+ NYql::TAstParseResult res = SqlToYql("select *, LENGTH(value) from plato.Input;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Unable to use plain '*' with other projection items. Please use qualified asterisk instead: '<table>.*' (<table> can be either table name or table alias).\n");
+ }
+
+ Y_UNIT_TEST(AsteriskWithSomethingBefore) {
+ NYql::TAstParseResult res = SqlToYql("select LENGTH(value), * from plato.Input;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:23: Error: Unable to use plain '*' with other projection items. Please use qualified asterisk instead: '<table>.*' (<table> can be either table name or table alias).\n");
+ }
+
+ Y_UNIT_TEST(DuplicatedQualifiedAsterisk) {
+ NYql::TAstParseResult res = SqlToYql("select in.*, key, in.* from plato.Input as in;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: Unable to use twice same quialified asterisk. Invalid source: in\n");
+ }
+
+ Y_UNIT_TEST(BrokenLabel) {
+ NYql::TAstParseResult res = SqlToYql("select in.*, key as `funny.label` from plato.Input as in;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:14: Error: Unable to use '.' in column name. Invalid column name: funny.label\n");
+ }
+
+ Y_UNIT_TEST(KeyConflictDetect0) {
+ NYql::TAstParseResult res = SqlToYql("select key, in.key as key from plato.Input as in;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:13: Error: Unable to use duplicate column names. Collision in name: key\n");
+ }
+
+ Y_UNIT_TEST(KeyConflictDetect1) {
+ NYql::TAstParseResult res = SqlToYql("select length(key) as key, key from plato.Input;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:28: Error: Unable to use duplicate column names. Collision in name: key\n");
+ }
+
+ Y_UNIT_TEST(KeyConflictDetect2) {
+ NYql::TAstParseResult res = SqlToYql("select key, in.key from plato.Input as in;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
+ }
+
+ Y_UNIT_TEST(AutogenerationAliasWithCollisionConflict1) {
+ UNIT_ASSERT(SqlToYql("select LENGTH(Value), key as column0 from plato.Input;").IsOk());
+ }
+
+ Y_UNIT_TEST(AutogenerationAliasWithCollisionConflict2) {
+ UNIT_ASSERT(SqlToYql("select key as column1, LENGTH(Value) from plato.Input;").IsOk());
+ }
+
+ Y_UNIT_TEST(MissedSourceTableForQualifiedAsteriskOnSimpleSelect) {
+ NYql::TAstParseResult res = SqlToYql("use plato; select Intop.*, Input.key from plato.Input;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: Unknown correlation name: Intop\n");
+ }
+
+ Y_UNIT_TEST(MissedSourceTableForQualifiedAsteriskOnJoin) {
+ NYql::TAstParseResult res = SqlToYql("use plato; select tmissed.*, t2.*, t1.key from plato.Input as t1 join plato.Input as t2 on t1.key==t2.key;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:19: Error: Unknown correlation name for asterisk: tmissed\n");
+ }
+
+ Y_UNIT_TEST(UnableToReferenceOnNotExistSubcolumn) {
+ NYql::TAstParseResult res = SqlToYql("select b.subkey from (select key from plato.Input as a) as b;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Column subkey is not in source column set\n");
+ }
+
+ Y_UNIT_TEST(ConflictOnSameNameWithQualify0) {
+ NYql::TAstParseResult res = SqlToYql("select in.key, in.key as key from plato.Input as in;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
+ }
+
+ Y_UNIT_TEST(ConflictOnSameNameWithQualify1) {
+ NYql::TAstParseResult res = SqlToYql("select in.key, length(key) as key from plato.Input as in;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
+ }
+
+ Y_UNIT_TEST(ConflictOnSameNameWithQualify2) {
+ NYql::TAstParseResult res = SqlToYql("select key, in.key from plato.Input as in;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
+ }
+
+ Y_UNIT_TEST(ConflictOnSameNameWithQualify3) {
+ NYql::TAstParseResult res = SqlToYql("select in.key, subkey as key from plato.Input as in;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Duplicate column: key\n");
+ }
+
+ Y_UNIT_TEST(SelectFlattenBySameColumns) {
+ NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key, key as kk)");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:46: Error: Duplicate column name found: key in FlattenBy section\n");
+ }
+
+ Y_UNIT_TEST(SelectFlattenBySameAliases) {
+ NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key as kk, subkey as kk);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Error: Duplicate alias found: kk in FlattenBy section\n");
+ }
+
+ Y_UNIT_TEST(SelectFlattenByExprSameAliases) {
+ NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key as kk, ListSkip(subkey,1) as kk);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Error: Collision between alias and column name: kk in FlattenBy section\n");
+ }
+
+ Y_UNIT_TEST(SelectFlattenByConflictNameAndAlias0) {
+ NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key, subkey as key);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:46: Error: Collision between alias and column name: key in FlattenBy section\n");
+ }
+
+ Y_UNIT_TEST(SelectFlattenByConflictNameAndAlias1) {
+ NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key as kk, subkey as key);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Error: Collision between alias and column name: key in FlattenBy section\n");
+ }
+
+ Y_UNIT_TEST(SelectFlattenByExprConflictNameAndAlias1) {
+ NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key as kk, ListSkip(subkey,1) as key);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Error: Duplicate column name found: key in FlattenBy section\n");
+ }
+
+ Y_UNIT_TEST(SelectFlattenByUnnamedExpr) {
+ NYql::TAstParseResult res = SqlToYql("select key from plato.Input flatten by (key, ListSkip(key, 1))");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:46: Error: Unnamed expression after FLATTEN BY is not allowed\n");
+ }
+
+ Y_UNIT_TEST(UseInOnStrings) {
+ NYql::TAstParseResult res = SqlToYql("select * from plato.Input where \"foo\" in \"foovalue\";");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:42: Error: Unable to use IN predicate with string argument, it won't search substring - "
+ "expecting tuple, list, dict or single column table source\n");
+ }
+
+ Y_UNIT_TEST(UseSubqueryInScalarContextInsideIn) {
+ NYql::TAstParseResult res = SqlToYql("$q = (select key from plato.Input); select * from plato.Input where subkey in ($q);");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:79: Warning: Using subrequest in scalar context after IN, "
+ "perhaps you should remove parenthesis here, code: 4501\n");
+ }
+
+ Y_UNIT_TEST(InHintsWithKeywordClash) {
+ NYql::TAstParseResult res = SqlToYql("SELECT COMPACT FROM plato.Input WHERE COMPACT IN COMPACT `COMPACT`(1,2,3)");
+ UNIT_ASSERT(!res.Root);
+ // should try to parse last compact as call expression
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:58: Error: Unknown builtin: COMPACT\n");
+ }
+
+ Y_UNIT_TEST(ErrorColumnPosition) {
+ NYql::TAstParseResult res = SqlToYql(
+ "USE plato;\n"
+ "SELECT \n"
+ "value FROM (\n"
+ "select key from Input\n"
+ ");\n"
+ );
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:1: Error: Column value is not in source column set\n");
+ }
+
+ Y_UNIT_TEST(PrimaryViewAbortMapReduce) {
+ NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input VIEW PRIMARY KEY");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: primary view is not supported for yt tables\n");
+ }
+
+ Y_UNIT_TEST(InsertAbortMapReduce) {
+ NYql::TAstParseResult res = SqlToYql("INSERT OR ABORT INTO plato.Output SELECT key FROM plato.Input");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: INSERT OR ABORT INTO is not supported for yt tables\n");
+ }
+
+ Y_UNIT_TEST(ReplaceIntoMapReduce) {
+ NYql::TAstParseResult res = SqlToYql("REPLACE INTO plato.Output SELECT key FROM plato.Input");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: Meaning of REPLACE INTO has been changed, now you should use INSERT INTO <table> WITH TRUNCATE ... for yt\n");
+ }
+
+ Y_UNIT_TEST(UpsertIntoMapReduce) {
+ NYql::TAstParseResult res = SqlToYql("UPSERT INTO plato.Output SELECT key FROM plato.Input");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: UPSERT INTO is not supported for yt tables\n");
+ }
+
+ Y_UNIT_TEST(UpdateMapReduce) {
+ NYql::TAstParseResult res = SqlToYql("UPDATE plato.Output SET value = value + 1 WHERE key < 1");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: UPDATE is unsupported for yt\n");
+ }
+
+ Y_UNIT_TEST(DeleteMapReduce) {
+ NYql::TAstParseResult res = SqlToYql("DELETE FROM plato.Output WHERE key < 1");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: DELETE is unsupported for yt\n");
+ }
+
+ Y_UNIT_TEST(ReplaceIntoWithTruncate) {
+ NYql::TAstParseResult res = SqlToYql("REPLACE INTO plato.Output WITH TRUNCATE SELECT key FROM plato.Input");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:32: Error: Unable REPLACE INTO with truncate mode\n");
+ }
+
+ Y_UNIT_TEST(UpsertIntoWithTruncate) {
+ NYql::TAstParseResult res = SqlToYql("UPSERT INTO plato.Output WITH TRUNCATE SELECT key FROM plato.Input");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:31: Error: Unable UPSERT INTO with truncate mode\n");
+ }
+
+ Y_UNIT_TEST(InsertIntoWithTruncateKikimr) {
+ NYql::TAstParseResult res = SqlToYql("INSERT INTO plato.Output WITH TRUNCATE SELECT key FROM plato.Input", 10, TString(NYql::KikimrProviderName));
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: INSERT INTO WITH TRUNCATE is not supported for kikimr tables\n");
+ }
+
+ Y_UNIT_TEST(InsertIntoWithWrongArgumentCount) {
+ NYql::TAstParseResult res = SqlToYql("insert into plato.Output with truncate (key, value, subkey) values (5, '1', '2', '3');");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:53: Error: VALUES have 4 columns, INSERT INTO ... WITH TRUNCATE expects: 3\n");
+ }
+
+ Y_UNIT_TEST(UpsertWithWrongArgumentCount) {
+ NYql::TAstParseResult res = SqlToYql("upsert into plato.Output (key, value, subkey) values (2, '3');", 10, TString(NYql::KikimrProviderName));
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:39: Error: VALUES have 2 columns, UPSERT INTO expects: 3\n");
+ }
+
+ Y_UNIT_TEST(GroupingSetByExprWithoutAlias) {
+ NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY GROUPING SETS (cast(key as uint32), subkey);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:53: Error: Unnamed expressions are not supported in GROUPING SETS. Please use '<expr> AS <name>'.\n");
+ }
+
+ Y_UNIT_TEST(GroupingSetByExprWithoutAlias2) {
+ NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY subkey || subkey, GROUPING SETS (\n"
+ "cast(key as uint32), subkey);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:1: Error: Unnamed expressions are not supported in GROUPING SETS. Please use '<expr> AS <name>'.\n");
+ }
+
+ Y_UNIT_TEST(CubeByExprWithoutAlias) {
+ NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY CUBE (key, subkey / key);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:56: Error: Unnamed expressions are not supported in CUBE. Please use '<expr> AS <name>'.\n");
+ }
+
+ Y_UNIT_TEST(RollupByExprWithoutAlias) {
+ NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY ROLLUP (subkey / key);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:53: Error: Unnamed expressions are not supported in ROLLUP. Please use '<expr> AS <name>'.\n");
+ }
+
+ Y_UNIT_TEST(GroupByHugeCubeDeniedNoPragma) {
+ NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY CUBE (key, subkey, value, key + subkey as sum, key - subkey as sub, key + val as keyval);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:119: Error: GROUP BY CUBE is allowed only for 5 columns, but you use 6\n");
+ }
+
+ Y_UNIT_TEST(GroupByInvalidPragma) {
+ NYql::TAstParseResult res = SqlToYql("PRAGMA GroupByCubeLimit = '-4';");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:27: Error: Expected unsigned integer literal as a single argument for: GroupByCubeLimit\n");
+ }
+
+ Y_UNIT_TEST(GroupByHugeCubeDeniedPragme) {
+ NYql::TAstParseResult res = SqlToYql("PRAGMA GroupByCubeLimit = '4'; SELECT key FROM plato.Input GROUP BY CUBE (key, subkey, value, key + subkey as sum, key - subkey as sub);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:132: Error: GROUP BY CUBE is allowed only for 4 columns, but you use 5\n");
+ }
+
+ Y_UNIT_TEST(GroupByFewBigCubes) {
+ NYql::TAstParseResult res = SqlToYql("SELECT key FROM plato.Input GROUP BY CUBE(key, subkey, key + subkey as sum), CUBE(value, value + key + subkey as total);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Unable to GROUP BY more than 64 groups, you try use 80 groups\n");
+ }
+
+ Y_UNIT_TEST(GroupByFewBigCubesWithPragmaLimit) {
+ NYql::TAstParseResult res = SqlToYql("PRAGMA GroupByLimit = '16'; SELECT key FROM plato.Input GROUP BY GROUPING SETS(key, subkey, key + subkey as sum), ROLLUP(value, value + key + subkey as total);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:29: Error: Unable to GROUP BY more than 16 groups, you try use 18 groups\n");
+ }
+
+ Y_UNIT_TEST(NoGroupingColumn0) {
+ NYql::TAstParseResult res = SqlToYql(
+ "select count(1), key_first, val_first, grouping(key_first, val_first, nomind) as group\n"
+ "from plato.Input group by grouping sets (cast(key as uint32) /100 as key_first, Substring(value, 1, 1) as val_first);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:71: Error: Column 'nomind' is not a grouping column\n");
+ }
+
+ Y_UNIT_TEST(NoGroupingColumn1) {
+ NYql::TAstParseResult res = SqlToYql("select count(1), grouping(key, value) as group_duo from plato.Input group by cube (key, subkey);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:32: Error: Column 'value' is not a grouping column\n");
+ }
+
+ Y_UNIT_TEST(EmptyAccess0) {
+ NYql::TAstParseResult res = SqlToYql("insert into plato.Output (list0, list1) values (AsList(0, 1, 2), AsList(``));");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:73: Error: Column reference \"\" is not allowed in current scope\n");
+ }
+
+ Y_UNIT_TEST(EmptyAccess1) {
+ NYql::TAstParseResult res = SqlToYql("insert into plato.Output (list0, list1) values (AsList(0, 1, 2), ``);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:66: Error: Column reference \"\" is not allowed in current scope\n");
+ }
+
+ Y_UNIT_TEST(UseUnknownColumnInInsert) {
+ NYql::TAstParseResult res = SqlToYql("insert into plato.Output (list0, list1) values (AsList(0, 1, 2), AsList(`test`));");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:73: Error: Column reference \"test\" is not allowed in current scope\n");
+ }
+
+ Y_UNIT_TEST(GroupByEmptyColumn) {
+ NYql::TAstParseResult res = SqlToYql("select count(1) from plato.Input group by ``;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:43: Error: Column name can not be empty\n");
+ }
+
+ Y_UNIT_TEST(ConvertNumberOutOfBase) {
+ NYql::TAstParseResult res = SqlToYql("select 0o80l;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to parse number from string: 0o80l, char: '8' is out of base: 8\n");
+ }
+
+ Y_UNIT_TEST(ConvertNumberOutOfRangeForInt64ButFitsInUint64) {
+ NYql::TAstParseResult res = SqlToYql("select 0xc000000000000000l;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to parse 13835058055282163712 as integer literal of Int64 type: value out of range for Int64\n");
+ }
+
+ Y_UNIT_TEST(ConvertNumberOutOfRangeUint64) {
+ NYql::TAstParseResult res = SqlToYql("select 0xc0000000000000000l;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to parse number from string: 0xc0000000000000000l, number limit overflow\n");
+
+ res = SqlToYql("select 1234234543563435151456;\n");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to parse number from string: 1234234543563435151456, number limit overflow\n");
+ }
+
+ Y_UNIT_TEST(ConvertNumberNegativeOutOfRange) {
+ NYql::TAstParseResult res = SqlToYql("select -9223372036854775808;\n"
+ "select -9223372036854775809;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:8: Error: Failed to parse negative integer: -9223372036854775809, number limit overflow\n");
+ }
+
+ Y_UNIT_TEST(InvaildUsageReal0) {
+ NYql::TAstParseResult res = SqlToYql("select .0;");
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:7: Error: Unexpected token '.' : cannot match to any predicted input...\n\n");
+#else
+ UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "<main>:1:7: Error: extraneous input '.' expecting {");
+#endif
+ }
+
+ Y_UNIT_TEST(InvaildUsageReal1) {
+ NYql::TAstParseResult res = SqlToYql("select .0f;");
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:7: Error: Unexpected token '.' : cannot match to any predicted input...\n\n");
+#else
+ UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "<main>:1:7: Error: extraneous input '.' expecting {");
+#endif
+ }
+
+ Y_UNIT_TEST(InvaildUsageWinFunctionWithoutWindow) {
+ NYql::TAstParseResult res = SqlToYql("select lead(key, 2) from plato.Input;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Failed to use window function Lead without window specification\n");
+ }
+
+ Y_UNIT_TEST(DropTableWithIfExists) {
+ NYql::TAstParseResult res = SqlToYql("DROP TABLE IF EXISTS plato.foo;");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop_if_exists"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(TooManyErrors) {
+ const char* q = R"(
+ USE plato;
+ select A, B, C, D, E, F, G, H, I, J, K, L, M, N from (select b from `abc`);
+)";
+
+ NYql::TAstParseResult res = SqlToYql(q, 10);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res),
+ R"(<main>:3:16: Error: Column A is not in source column set. Did you mean b?
+<main>:3:19: Error: Column B is not in source column set. Did you mean b?
+<main>:3:22: Error: Column C is not in source column set. Did you mean b?
+<main>:3:25: Error: Column D is not in source column set. Did you mean b?
+<main>:3:28: Error: Column E is not in source column set. Did you mean b?
+<main>:3:31: Error: Column F is not in source column set. Did you mean b?
+<main>:3:34: Error: Column G is not in source column set. Did you mean b?
+<main>:3:37: Error: Column H is not in source column set. Did you mean b?
+<main>:3:40: Error: Column I is not in source column set. Did you mean b?
+<main>: Error: Too many issues, code: 1
+)");
+ };
+
+ Y_UNIT_TEST(ShouldCloneBindingForNamedParameter) {
+ NYql::TAstParseResult res = SqlToYql(R"($f = () -> {
+ $value_type = TypeOf(1);
+ $pair_type = StructType(
+ TypeOf("2") AS key,
+ $value_type AS value
+ );
+
+ RETURN TupleType(
+ ListType($value_type),
+ $pair_type);
+};
+
+select FormatType($f());
+)");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(BlockedInvalidFrameBounds) {
+ auto check = [](const TString& frame, const TString& err) {
+ const TString prefix = "SELECT SUM(x) OVER w FROM plato.Input WINDOW w AS (PARTITION BY key ORDER BY subkey\n";
+ NYql::TAstParseResult res = SqlToYql(prefix + frame + ")");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), err);
+ };
+
+ check("ROWS UNBOUNDED FOLLOWING", "<main>:2:5: Error: Frame cannot start from UNBOUNDED FOLLOWING\n");
+ check("ROWS BETWEEN 5 PRECEDING AND UNBOUNDED PRECEDING", "<main>:2:29: Error: Frame cannot end with UNBOUNDED PRECEDING\n");
+ check("ROWS BETWEEN CURRENT ROW AND 5 PRECEDING", "<main>:2:13: Error: Frame cannot start from CURRENT ROW and end with PRECEDING\n");
+ check("ROWS BETWEEN 5 FOLLOWING AND CURRENT ROW", "<main>:2:14: Error: Frame cannot start from FOLLOWING and end with CURRENT ROW\n");
+ check("ROWS BETWEEN 5 FOLLOWING AND 5 PRECEDING", "<main>:2:14: Error: Frame cannot start from FOLLOWING and end with PRECEDING\n");
+ }
+
+ Y_UNIT_TEST(BlockedRangeValueWithoutSingleOrderBy) {
+ UNIT_ASSERT(SqlToYql("SELECT COUNT(*) OVER (RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM plato.Input").IsOk());
+ UNIT_ASSERT(SqlToYql("SELECT COUNT(*) OVER (RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM plato.Input").IsOk());
+
+ auto res = SqlToYql("SELECT COUNT(*) OVER (RANGE 5 PRECEDING) FROM plato.Input");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:29: Error: RANGE with <offset> PRECEDING/FOLLOWING requires exactly one expression in ORDER BY partition clause\n");
+
+ res = SqlToYql("SELECT COUNT(*) OVER (ORDER BY key, value RANGE 5 PRECEDING) FROM plato.Input");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Error: RANGE with <offset> PRECEDING/FOLLOWING requires exactly one expression in ORDER BY partition clause\n");
+ }
+
+ Y_UNIT_TEST(NoColumnsInFrameBounds) {
+ NYql::TAstParseResult res = SqlToYql(
+ "SELECT SUM(x) OVER w FROM plato.Input WINDOW w AS (ROWS BETWEEN\n"
+ " 1 + key PRECEDING AND 2 + key FOLLOWING);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:6: Error: Column reference \"key\" is not allowed in current scope\n");
+ }
+
+ Y_UNIT_TEST(WarnOnEmptyFrameBounds) {
+ NYql::TAstParseResult res = SqlToYql(
+ "SELECT SUM(x) OVER w FROM plato.Input WINDOW w AS (PARTITION BY key ORDER BY subkey\n"
+ "ROWS BETWEEN 10 FOLLOWING AND 5 FOLLOWING)");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:14: Warning: Used frame specification implies empty window frame, code: 4520\n");
+ }
+
+ Y_UNIT_TEST(WarnOnRankWithUnorderedWindow) {
+ NYql::TAstParseResult res = SqlToYql("SELECT RANK() OVER w FROM plato.Input WINDOW w AS ()");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Warning: Rank() is used with unordered window - all rows will be considered equal to each other, code: 4521\n");
+ }
+
+ Y_UNIT_TEST(WarnOnRankExprWithUnorderedWindow) {
+ NYql::TAstParseResult res = SqlToYql("SELECT RANK(key) OVER w FROM plato.Input WINDOW w AS ()");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Warning: Rank(<expression>) is used with unordered window - the result is likely to be undefined, code: 4521\n");
+ }
+
+ Y_UNIT_TEST(AnyAsTableName) {
+ NYql::TAstParseResult res = SqlToYql("use plato; select * from any;");
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:28: Error: Unexpected token ';' : syntax error...\n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:28: Error: no viable alternative at input 'any;'\n");
+#endif
+ }
+
+ Y_UNIT_TEST(IncorrectOrderOfLambdaOptionalArgs) {
+ NYql::TAstParseResult res = SqlToYql("$f = ($x?, $y)->($x + $y); select $f(1);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Non-optional argument can not follow optional one\n");
+ }
+
+ Y_UNIT_TEST(IncorrectOrderOfActionOptionalArgs) {
+ NYql::TAstParseResult res = SqlToYql("define action $f($x?, $y) as select $x,$y; end define; do $f(1);");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:23: Error: Non-optional argument can not follow optional one\n");
+ }
+
+ Y_UNIT_TEST(NotAllowedQuestionOnNamedNode) {
+ NYql::TAstParseResult res = SqlToYql("$f = 1; select $f?;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:18: Error: Unexpected token '?' at the end of expression\n");
+ }
+
+ Y_UNIT_TEST(AnyAndCrossJoin) {
+ NYql::TAstParseResult res = SqlToYql("use plato; select * from any Input1 cross join Input2");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:26: Error: ANY should not be used with Cross JOIN\n");
+
+ res = SqlToYql("use plato; select * from Input1 cross join any Input2");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:44: Error: ANY should not be used with Cross JOIN\n");
+ }
+
+ Y_UNIT_TEST(AnyWithCartesianProduct) {
+ NYql::TAstParseResult res = SqlToYql("pragma AnsiImplicitCrossJoin; use plato; select * from any Input1, Input2");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:56: Error: ANY should not be used with Cross JOIN\n");
+
+ res = SqlToYql("pragma AnsiImplicitCrossJoin; use plato; select * from Input1, any Input2");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:64: Error: ANY should not be used with Cross JOIN\n");
+ }
+
+ Y_UNIT_TEST(ErrorPlainEndAsInlineActionTerminator) {
+ NYql::TAstParseResult res = SqlToYql(
+ "do begin\n"
+ " select 1\n"
+ "; end\n");
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:0: Error: Unexpected token absence : Missing DO \n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:0: Error: missing DO at '<EOF>'\n");
+#endif
+ }
+
+ Y_UNIT_TEST(ErrorMultiWayJoinWithUsing) {
+ NYql::TAstParseResult res = SqlToYql(
+ "USE plato;\n"
+ "PRAGMA DisableSimpleColumns;\n"
+ "SELECT *\n"
+ "FROM Input1 AS a\n"
+ "JOIN Input2 AS b USING(key)\n"
+ "JOIN Input3 AS c ON a.key = c.key;\n"
+ );
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res),
+ "<main>:5:24: Error: Multi-way JOINs should be connected with ON clause instead of USING clause\n"
+ );
+ }
+
+ Y_UNIT_TEST(RequireLabelInFlattenByWithDot) {
+ NYql::TAstParseResult res = SqlToYql("select * from plato.Input flatten by x.y");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res),
+ "<main>:1:40: Error: Unnamed expression after FLATTEN BY is not allowed\n"
+ );
+ }
+
+ Y_UNIT_TEST(WarnUnnamedColumns) {
+ NYql::TAstParseResult res = SqlToYql(
+ "PRAGMA WarnUnnamedColumns;\n"
+ "\n"
+ "SELECT key, subkey, key || subkey FROM plato.Input ORDER BY subkey;\n");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:28: Warning: Autogenerated column name column2 will be used for expression, code: 4516\n");
+ }
+
+ Y_UNIT_TEST(WarnSourceColumnMismatch) {
+ NYql::TAstParseResult res = SqlToYql(
+ "insert into plato.Output (key, subkey, new_value, one_more_value) select key as Key, subkey, value, \"x\" from plato.Input;");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:51: Warning: Column names in SELECT don't match column specification in parenthesis. \"key\" doesn't match \"Key\". \"new_value\" doesn't match \"value\", code: 4517\n");
+ }
+
+ Y_UNIT_TEST(YtCaseInsensitive) {
+ NYql::TAstParseResult res = SqlToYql("select * from PlatO.foo;");
+ UNIT_ASSERT(res.Root);
+
+ res = SqlToYql("use PlatO; select * from foo;");
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(KikimrCaseSensitive) {
+ NYql::TAstParseResult res = SqlToYql("select * from PlatO.foo;", 10, "kikimr");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:15: Error: Unknown cluster: PlatO\n");
+
+ res = SqlToYql("use PlatO; select * from foo;", 10, "kikimr");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:5: Error: Unknown cluster: PlatO\n");
+ }
+
+ Y_UNIT_TEST(DiscoveryModeForbidden) {
+ NYql::TAstParseResult res = SqlToYqlWithMode("insert into plato.Output select * from plato.range(\"\", Input1, Input4)", NSQLTranslation::ESqlMode::DISCOVERY);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: range is not allowed in Discovery mode, code: 4600\n");
+
+ res = SqlToYqlWithMode("insert into plato.Output select * from plato.like(\"\", \"Input%\")", NSQLTranslation::ESqlMode::DISCOVERY);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: like is not allowed in Discovery mode, code: 4600\n");
+
+ res = SqlToYqlWithMode("insert into plato.Output select * from plato.regexp(\"\", \"Input.\")", NSQLTranslation::ESqlMode::DISCOVERY);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: regexp is not allowed in Discovery mode, code: 4600\n");
+
+ res = SqlToYqlWithMode("insert into plato.Output select * from plato.filter(\"\", ($name) -> { return find($name, \"Input\") is not null; })", NSQLTranslation::ESqlMode::DISCOVERY);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: filter is not allowed in Discovery mode, code: 4600\n");
+
+ res = SqlToYqlWithMode("select Path from plato.folder(\"\") where Type == \"table\"", NSQLTranslation::ESqlMode::DISCOVERY);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:18: Error: folder is not allowed in Discovery mode, code: 4600\n");
+ }
+
+ Y_UNIT_TEST(YsonFuncWithoutArgs) {
+ UNIT_ASSERT(SqlToYql("SELECT Yson::SerializeText(Yson::From());").IsOk());
+ }
+
+ Y_UNIT_TEST(CanNotUseOrderByInNonLastSelectInUnionAllChain) {
+ auto req = "pragma AnsiOrderByLimitInUnionAll;\n"
+ "use plato;\n"
+ "\n"
+ "select * from Input order by key\n"
+ "union all\n"
+ "select * from Input order by key limit 1;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:21: Error: ORDER BY within UNION ALL is only allowed after last subquery\n");
+ }
+
+ Y_UNIT_TEST(CanNotUseLimitInNonLastSelectInUnionAllChain) {
+ auto req = "pragma AnsiOrderByLimitInUnionAll;\n"
+ "use plato;\n"
+ "\n"
+ "select * from Input limit 1\n"
+ "union all\n"
+ "select * from Input order by key limit 1;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:21: Error: LIMIT within UNION ALL is only allowed after last subquery\n");
+ }
+
+ Y_UNIT_TEST(CanNotUseDiscardInNonFirstSelectInUnionAllChain) {
+ auto req = "pragma AnsiOrderByLimitInUnionAll;\n"
+ "use plato;\n"
+ "\n"
+ "select * from Input\n"
+ "union all\n"
+ "discard select * from Input;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:6:1: Error: DISCARD within UNION ALL is only allowed before first subquery\n");
+ }
+
+ Y_UNIT_TEST(CanNotUseIntoResultInNonLastSelectInUnionAllChain) {
+ auto req = "use plato;\n"
+ "pragma AnsiOrderByLimitInUnionAll;\n"
+ "\n"
+ "select * from Input\n"
+ "union all\n"
+ "discard select * from Input;";
+
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:6:1: Error: DISCARD within UNION ALL is only allowed before first subquery\n");
+ }
+
+ Y_UNIT_TEST(YsonStrictInvalidPragma) {
+ auto res = SqlToYql("pragma yson.Strict = \"wrong\";");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:22: Error: Expected 'true', 'false' or no parameter for: Strict\n");
+ }
+
+ Y_UNIT_TEST(WarnTableNameInSomeContexts) {
+ UNIT_ASSERT(SqlToYql("use plato; select TableName() from Input;").IsOk());
+ UNIT_ASSERT(SqlToYql("use plato; select TableName(\"aaaa\");").IsOk());
+ UNIT_ASSERT(SqlToYql("select TableName(\"aaaa\", \"yt\");").IsOk());
+
+ auto res = SqlToYql("select TableName() from plato.Input;");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: TableName requires either service name as second argument or current cluster name\n");
+
+ res = SqlToYql("use plato;\n"
+ "select TableName() from Input1 as a join Input2 as b using(key);");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:8: Warning: TableName() may produce empty result when used in ambiguous context (with JOIN), code: 4525\n");
+
+ res = SqlToYql("use plato;\n"
+ "select SOME(TableName()), key from Input group by key;");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:13: Warning: TableName() will produce empty result when used with aggregation.\n"
+ "Please consult documentation for possible workaround, code: 4525\n");
+ }
+
+ Y_UNIT_TEST(WarnOnDistincWithHavingWithoutAggregations) {
+ auto res = SqlToYql("select distinct key from plato.Input having key != '0';");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Warning: The usage of HAVING without aggregations with SELECT DISTINCT is non-standard and will stop working soon. Please use WHERE instead., code: 4526\n");
+ }
+
+ Y_UNIT_TEST(FlattenByExprWithNestedNull) {
+ auto res = SqlToYql("USE plato;\n"
+ "\n"
+ "SELECT * FROM (SELECT 1 AS region_id)\n"
+ "FLATTEN BY (\n"
+ " CAST($unknown(region_id) AS List<String>) AS region\n"
+ ")");
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:10: Error: Unknown name: $unknown\n");
+ }
+
+ Y_UNIT_TEST(EmptySymbolNameIsForbidden) {
+ auto req = " $`` = 1; select $``;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:5: Error: Empty symbol name is not allowed\n");
+ }
+
+ Y_UNIT_TEST(WarnOnBinaryOpWithNullArg) {
+ auto req = "select * from plato.Input where cast(key as Int32) != NULL";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:52: Warning: Binary operation != will return NULL here, code: 4529\n");
+
+ req = "select 1 or null";
+ res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "");
+ }
+
+ Y_UNIT_TEST(ErrorIfTableSampleArgUsesColumns) {
+ auto req = "SELECT key FROM plato.Input TABLESAMPLE BERNOULLI(MIN_OF(100.0, CAST(subkey as Int32)));";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:70: Error: Column reference \"subkey\" is not allowed in current scope\n");
+ }
+
+ Y_UNIT_TEST(DerivedColumnListForSelectIsNotSupportedYet) {
+ auto req = "SELECT a,b,c FROM plato.Input as t(x,y,z);";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:35: Error: Derived column list is only supported for VALUES\n");
+ }
+
+ Y_UNIT_TEST(ErrorIfValuesHasDifferentCountOfColumns) {
+ auto req = "VALUES (1,2,3), (4,5);";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: All VALUES items should have same size: expecting 3, got 2\n");
+ }
+
+ Y_UNIT_TEST(ErrorIfDerivedColumnSizeExceedValuesColumnCount) {
+ auto req = "SELECT * FROM(VALUES (1,2), (3,4)) as t(x,y,z);";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:40: Error: Derived column list size exceeds column count in VALUES\n");
+ }
+
+ Y_UNIT_TEST(WarnoOnAutogeneratedNamesForValues) {
+ auto req = "PRAGMA WarnUnnamedColumns;\n"
+ "SELECT * FROM (VALUES (1,2,3,4), (5,6,7,8)) as t(x,y);";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:16: Warning: Autogenerated column names column2...column3 will be used here, code: 4516\n");
+ }
+
+ Y_UNIT_TEST(ErrUnionAllWithOrderByWithoutExplicitLegacyMode) {
+ auto req = "use plato;\n"
+ "\n"
+ "select * from Input order by key\n"
+ "union all\n"
+ "select * from Input order by key;";
+
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:21: Error: ORDER BY within UNION ALL is only allowed after last subquery\n");
+ }
+
+ Y_UNIT_TEST(ErrUnionAllWithLimitWithoutExplicitLegacyMode) {
+ auto req = "use plato;\n"
+ "\n"
+ "select * from Input limit 10\n"
+ "union all\n"
+ "select * from Input limit 1;";
+
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:21: Error: LIMIT within UNION ALL is only allowed after last subquery\n");
+ }
+
+ Y_UNIT_TEST(ErrUnionAllWithIntoResultWithoutExplicitLegacyMode) {
+ auto req = "use plato;\n"
+ "\n"
+ "select * from Input into result aaa\n"
+ "union all\n"
+ "select * from Input;";
+
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:21: Error: INTO RESULT within UNION ALL is only allowed after last subquery\n");
+ }
+
+ Y_UNIT_TEST(ErrUnionAllWithDiscardWithoutExplicitLegacyMode) {
+ auto req = "use plato;\n"
+ "\n"
+ "select * from Input\n"
+ "union all\n"
+ "discard select * from Input;";
+
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:1: Error: DISCARD within UNION ALL is only allowed before first subquery\n");
+ }
+
+ Y_UNIT_TEST(ErrUnionAllKeepsIgnoredOrderByWarning) {
+ auto req = "use plato;\n"
+ "\n"
+ "SELECT * FROM (\n"
+ " SELECT * FROM Input\n"
+ " UNION ALL\n"
+ " SELECT t.* FROM Input AS t ORDER BY t.key\n"
+ ");";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:3: Warning: ORDER BY without LIMIT in subquery will be ignored, code: 4504\n"
+ "<main>:6:39: Error: Unknown correlation name: t\n");
+ }
+
+ Y_UNIT_TEST(ErrOrderByIgnoredButCheckedForMissingColumns) {
+ auto req = "$src = SELECT key FROM (SELECT 1 as key, 2 as subkey) ORDER BY x; SELECT * FROM $src;";
+ ExpectFailWithError(req, "<main>:1:8: Warning: ORDER BY without LIMIT in subquery will be ignored, code: 4504\n"
+ "<main>:1:64: Error: Column x is not in source column set\n");
+
+ req = "$src = SELECT key FROM plato.Input ORDER BY x; SELECT * FROM $src;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Warning: ORDER BY without LIMIT in subquery will be ignored, code: 4504\n");
+ }
+
+ Y_UNIT_TEST(InvalidTtlInterval) {
+ auto req = R"(
+ USE plato;
+ CREATE TABLE tableName (Key Uint32, CreatedAt Timestamp, PRIMARY KEY (Key))
+ WITH (TTL = 1 On CreatedAt);
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:25: Error: Literal of Interval type is expected for TTL\n"
+ "<main>:4:25: Error: Invalid TTL settings\n");
+ }
+
+ Y_UNIT_TEST(InvalidTtlUnit) {
+ auto req = R"(
+ USE plato;
+ CREATE TABLE tableName (Key Uint32, CreatedAt Uint32, PRIMARY KEY (Key))
+ WITH (TTL = Interval("P1D") On CreatedAt AS PICOSECONDS);
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "<main>:4:56: Error: Unexpected token 'PICOSECONDS'");
+#else
+ UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "mismatched input 'PICOSECONDS' expecting {MICROSECONDS, MILLISECONDS, NANOSECONDS, SECONDS}");
+#endif
+ }
+
+ Y_UNIT_TEST(InvalidChangefeedSink) {
+ auto req = R"(
+ USE plato;
+ CREATE TABLE tableName (
+ Key Uint32, PRIMARY KEY (Key),
+ CHANGEFEED feedName WITH (SINK_TYPE = "S3", MODE = "KEYS_ONLY", FORMAT = "json")
+ );
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:55: Error: Unknown changefeed sink type: S3\n");
+ }
+
+ Y_UNIT_TEST(InvalidChangefeedSettings) {
+ auto req = R"(
+ USE plato;
+ CREATE TABLE tableName (
+ Key Uint32, PRIMARY KEY (Key),
+ CHANGEFEED feedName WITH (SINK_TYPE = "local", FOO = "bar")
+ );
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:64: Error: Unknown changefeed setting: FOO\n");
+ }
+
+ Y_UNIT_TEST(InvalidChangefeedInitialScan) {
+ auto req = R"(
+ USE plato;
+ CREATE TABLE tableName (
+ Key Uint32, PRIMARY KEY (Key),
+ CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", INITIAL_SCAN = "foo")
+ );
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:95: Error: Literal of Bool type is expected for INITIAL_SCAN\n");
+ }
+
+ Y_UNIT_TEST(InvalidChangefeedVirtualTimestamps) {
+ auto req = R"(
+ USE plato;
+ CREATE TABLE tableName (
+ Key Uint32, PRIMARY KEY (Key),
+ CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", VIRTUAL_TIMESTAMPS = "foo")
+ );
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:101: Error: Literal of Bool type is expected for VIRTUAL_TIMESTAMPS\n");
+ }
+
+ Y_UNIT_TEST(InvalidChangefeedResolvedTimestamps) {
+ auto req = R"(
+ USE plato;
+ CREATE TABLE tableName (
+ Key Uint32, PRIMARY KEY (Key),
+ CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", BARRIERS_INTERVAL = "foo")
+ );
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:100: Error: Literal of Interval type is expected for BARRIERS_INTERVAL\n");
+ }
+
+ Y_UNIT_TEST(InvalidChangefeedRetentionPeriod) {
+ auto req = R"(
+ USE plato;
+ CREATE TABLE tableName (
+ Key Uint32, PRIMARY KEY (Key),
+ CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", RETENTION_PERIOD = "foo")
+ );
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:99: Error: Literal of Interval type is expected for RETENTION_PERIOD\n");
+ }
+
+ Y_UNIT_TEST(InvalidChangefeedTopicPartitions) {
+ auto req = R"(
+ USE plato;
+ CREATE TABLE tableName (
+ Key Uint32, PRIMARY KEY (Key),
+ CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", TOPIC_MIN_ACTIVE_PARTITIONS = "foo")
+ );
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:110: Error: Literal of integer type is expected for TOPIC_MIN_ACTIVE_PARTITIONS\n");
+ }
+
+ Y_UNIT_TEST(InvalidChangefeedAwsRegion) {
+ auto req = R"(
+ USE plato;
+ CREATE TABLE tableName (
+ Key Uint32, PRIMARY KEY (Key),
+ CHANGEFEED feedName WITH (MODE = "KEYS_ONLY", FORMAT = "json", AWS_REGION = true)
+ );
+ )";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:5:93: Error: Literal of String type is expected for AWS_REGION\n");
+ }
+
+ Y_UNIT_TEST(ErrJoinWithGroupingSetsWithoutCorrelationName) {
+ auto req = "USE plato;\n"
+ "\n"
+ "SELECT k1, k2, subkey\n"
+ "FROM T1 AS a JOIN T2 AS b USING (key)\n"
+ "GROUP BY GROUPING SETS(\n"
+ " (a.key as k1, b.subkey as k2),\n"
+ " (k1),\n"
+ " (subkey)\n"
+ ");";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:8:4: Error: Columns in grouping sets should have correlation name, error in key: subkey\n");
+ }
+
+ Y_UNIT_TEST(ErrJoinWithGroupByWithoutCorrelationName) {
+ auto req = "USE plato;\n"
+ "\n"
+ "SELECT k1, k2,\n"
+ " value\n"
+ "FROM T1 AS a JOIN T2 AS b USING (key)\n"
+ "GROUP BY a.key as k1, b.subkey as k2,\n"
+ " value;";
+ ExpectFailWithError(req,
+ "<main>:7:5: Error: Columns in GROUP BY should have correlation name, error in key: value\n");
+ }
+
+ Y_UNIT_TEST(ErrWithMissingFrom) {
+ auto req = "select 1 as key where 1 > 1;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:25: Error: Filtering is not allowed without FROM\n");
+
+ req = "select 1 + count(*);";
+ res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:12: Error: Aggregation is not allowed without FROM\n");
+
+ req = "select 1 as key, subkey + value;";
+ res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Column references are not allowed without FROM\n"
+ "<main>:1:18: Error: Column reference 'subkey'\n"
+ "<main>:1:1: Error: Column references are not allowed without FROM\n"
+ "<main>:1:27: Error: Column reference 'value'\n");
+
+ req = "select count(1) group by key;";
+ res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:1: Error: Column references are not allowed without FROM\n"
+ "<main>:1:26: Error: Column reference 'key'\n");
+ }
+
+ Y_UNIT_TEST(ErrWithMissingFromForWindow) {
+ auto req = "$c = () -> (1 + count(1) over w);\n"
+ "select $c();";
+ ExpectFailWithError(req,
+ "<main>:1:9: Error: Window and aggregation functions are not allowed in this context\n"
+ "<main>:1:17: Error: Failed to use aggregation function Count without window specification or in wrong place\n");
+
+ req = "$c = () -> (1 + lead(1) over w);\n"
+ "select $c();";
+ ExpectFailWithError(req,
+ "<main>:1:17: Error: Window functions are not allowed in this context\n"
+ "<main>:1:17: Error: Failed to use window function Lead without window specification or in wrong place\n");
+
+ req = "select 1 + count(1) over w window w as ();";
+ ExpectFailWithError(req,
+ "<main>:1:1: Error: Window and aggregation functions are not allowed without FROM\n"
+ "<main>:1:12: Error: Failed to use aggregation function Count without window specification or in wrong place\n");
+
+ req = "select 1 + lead(1) over w window w as ();";
+ ExpectFailWithError(req,
+ "<main>:1:12: Error: Window functions are not allowed without FROM\n"
+ "<main>:1:12: Error: Failed to use window function Lead without window specification or in wrong place\n");
+ }
+
+ Y_UNIT_TEST(ErrWithMissingFromForInplaceWindow) {
+ auto req = "$c = () -> (1 + count(1) over ());\n"
+ "select $c();";
+ ExpectFailWithError(req,
+ "<main>:1:26: Error: Window and aggregation functions are not allowed in this context\n");
+
+ req = "$c = () -> (1 + lead(1) over (rows between unbounded preceding and current row));\n"
+ "select $c();";
+ ExpectFailWithError(req,
+ "<main>:1:25: Error: Window and aggregation functions are not allowed in this context\n");
+
+ req = "select 1 + count(1) over ();";
+ ExpectFailWithError(req,
+ "<main>:1:1: Error: Window and aggregation functions are not allowed without FROM\n"
+ "<main>:1:12: Error: Failed to use aggregation function Count without window specification or in wrong place\n");
+
+ req = "select 1 + lead(1) over (rows between current row and unbounded following);";
+ ExpectFailWithError(req,
+ "<main>:1:12: Error: Window functions are not allowed without FROM\n"
+ "<main>:1:12: Error: Failed to use window function Lead without window specification or in wrong place\n");
+ }
+
+ Y_UNIT_TEST(ErrDistinctInWrongPlace) {
+ auto req = "select Some::Udf(distinct key) from plato.Input;";
+ ExpectFailWithError(req,
+ "<main>:1:18: Error: DISTINCT can only be used in aggregation functions\n");
+ req = "select sum(key)(distinct foo) from plato.Input;";
+ ExpectFailWithError(req,
+ "<main>:1:17: Error: DISTINCT can only be used in aggregation functions\n");
+
+ req = "select len(distinct foo) from plato.Input;";
+ ExpectFailWithError(req,
+ "<main>:1:8: Error: DISTINCT can only be used in aggregation functions\n");
+
+ req = "$foo = ($x) -> ($x); select $foo(distinct key) from plato.Input;";
+ ExpectFailWithError(req,
+ "<main>:1:34: Error: DISTINCT can only be used in aggregation functions\n");
+ }
+
+ Y_UNIT_TEST(ErrForNotSingleChildInInlineAST) {
+ ExpectFailWithError("select YQL::\"\"",
+ "<main>:1:8: Error: Failed to parse YQL: expecting AST root node with single child, but got 0\n");
+ ExpectFailWithError("select YQL::@@ \t@@",
+ "<main>:1:8: Error: Failed to parse YQL: expecting AST root node with single child, but got 0\n");
+ auto req = "$lambda = YQL::@@(lambda '(x)(+ x x)) (lambda '(y)(+ y y))@@;\n"
+ "select ListMap([1, 2, 3], $lambda);";
+ ExpectFailWithError(req,
+ "<main>:1:11: Error: Failed to parse YQL: expecting AST root node with single child, but got 2\n");
+ }
+
+ Y_UNIT_TEST(ErrEmptyColumnName) {
+ ExpectFailWithError("select * without \"\" from plato.Input",
+ "<main>:1:18: Error: String literal can not be used here\n");
+
+ ExpectFailWithError("select * without `` from plato.Input;",
+ "<main>:1:18: Error: Empty column name is not allowed\n");
+
+ ExpectFailWithErrorForAnsiLexer("select * without \"\" from plato.Input",
+ "<main>:1:18: Error: Empty column name is not allowed\n");
+
+ ExpectFailWithErrorForAnsiLexer("select * without `` from plato.Input;",
+ "<main>:1:18: Error: Empty column name is not allowed\n");
+ }
+
+ Y_UNIT_TEST(ErrOnNonZeroArgumentsForTableRows) {
+ ExpectFailWithError("$udf=\"\";process plato.Input using $udf(TableRows(k))",
+ "<main>:1:40: Error: TableRows requires exactly 0 arguments\n");
+ }
+
+ Y_UNIT_TEST(ErrGroupByWithAggregationFunctionAndDistinctExpr) {
+ ExpectFailWithError("select * from plato.Input group by count(distinct key|key)",
+ "<main>:1:36: Error: Unable to GROUP BY aggregated values\n");
+ }
+
+ // FIXME: check if we can get old behaviour
+#if 0
+ Y_UNIT_TEST(ErrWithSchemaWithColumnsWithoutType) {
+ ExpectFailWithError("select * from plato.Input with COLUMNs",
+ "<main>:1:32: Error: Expected type after COLUMNS\n"
+ "<main>:1:32: Error: Failed to parse table hints\n");
+
+ ExpectFailWithError("select * from plato.Input with scheMa",
+ "<main>:1:32: Error: Expected type after SCHEMA\n"
+ "<main>:1:32: Error: Failed to parse table hints\n");
+ }
+#endif
+
+ Y_UNIT_TEST(ErrCollectPreaggregatedInListLiteralWithoutFrom) {
+ ExpectFailWithError("SELECT([VARIANCE(DISTINCT[])])",
+ "<main>:1:1: Error: Column references are not allowed without FROM\n"
+ "<main>:1:9: Error: Column reference '_yql_preagg_Variance0'\n");
+ }
+
+ Y_UNIT_TEST(ErrGroupBySmartParenAsTuple) {
+ ExpectFailWithError("SELECT * FROM plato.Input GROUP BY (k, v,)",
+ "<main>:1:41: Error: Unexpected trailing comma in grouping elements list\n");
+ }
+
+ Y_UNIT_TEST(HandleNestedSmartParensInGroupBy) {
+ ExpectFailWithError("SELECT * FROM plato.Input GROUP BY (+() as k)",
+ "<main>:1:37: Error: Unable to GROUP BY constant expression\n");
+ }
+
+ Y_UNIT_TEST(ErrRenameWithAddColumn) {
+ ExpectFailWithError("USE plato; ALTER TABLE table RENAME TO moved, ADD COLUMN addc uint64",
+ "<main>:1:40: Error: RENAME TO can not be used together with another table action\n");
+ }
+
+ Y_UNIT_TEST(ErrAddColumnAndRename) {
+ // FIXME: fix positions in ALTER TABLE
+ ExpectFailWithError("USE plato; ALTER TABLE table ADD COLUMN addc uint64, RENAME TO moved",
+ "<main>:1:46: Error: RENAME TO can not be used together with another table action\n");
+ }
+
+ Y_UNIT_TEST(InvalidUuidValue) {
+ ExpectFailWithError("SELECT Uuid('123e4567ae89ba12d3aa456a426614174ab0')",
+ "<main>:1:8: Error: Invalid value \"123e4567ae89ba12d3aa456a426614174ab0\" for type Uuid\n");
+ ExpectFailWithError("SELECT Uuid('123e4567ae89b-12d3-a456-426614174000')",
+ "<main>:1:8: Error: Invalid value \"123e4567ae89b-12d3-a456-426614174000\" for type Uuid\n");
+ }
+
+ Y_UNIT_TEST(WindowFunctionWithoutOver) {
+ ExpectFailWithError("SELECT LAST_VALUE(foo) FROM plato.Input",
+ "<main>:1:8: Error: Can't use window function LastValue without window specification (OVER keyword is missing)\n");
+ ExpectFailWithError("SELECT LAST_VALUE(foo) FROM plato.Input GROUP BY key",
+ "<main>:1:8: Error: Can't use window function LastValue without window specification (OVER keyword is missing)\n");
+ }
+
+ Y_UNIT_TEST(CreateAlterUserWithoutCluster) {
+ ExpectFailWithError("\n CREATE USER user ENCRYPTED PASSWORD 'foobar';", "<main>:2:2: Error: USE statement is missing - no default cluster is selected\n");
+ ExpectFailWithError("ALTER USER CURRENT_USER RENAME TO $foo;", "<main>:1:1: Error: USE statement is missing - no default cluster is selected\n");
+ }
+
+ Y_UNIT_TEST(ModifyPermissionsWithoutCluster) {
+ ExpectFailWithError("\n GRANT CONNECT ON `/Root` TO user;", "<main>:2:2: Error: USE statement is missing - no default cluster is selected\n");
+ ExpectFailWithError("\n REVOKE MANAGE ON `/Root` FROM user;", "<main>:2:2: Error: USE statement is missing - no default cluster is selected\n");
+ }
+
+ Y_UNIT_TEST(ReservedRoleNames) {
+ ExpectFailWithError("USE plato; CREATE USER current_User;", "<main>:1:24: Error: System role CURRENT_USER can not be used here\n");
+ ExpectFailWithError("USE plato; ALTER USER current_User RENAME TO Current_role", "<main>:1:46: Error: System role CURRENT_ROLE can not be used here\n");
+ UNIT_ASSERT(SqlToYql("USE plato; DROP GROUP IF EXISTS a, b, c, current_User;").IsOk());
+ }
+
+ Y_UNIT_TEST(DisableClassicDivisionWithError) {
+ ExpectFailWithError("pragma ClassicDivision = 'false'; select $foo / 30;", "<main>:1:42: Error: Unknown name: $foo\n");
+ }
+
+ Y_UNIT_TEST(AggregationOfAgrregatedDistinctExpr) {
+ ExpectFailWithError("select sum(sum(distinct x + 1)) from plato.Input", "<main>:1:12: Error: Aggregation of aggregated values is forbidden\n");
+ }
+
+ Y_UNIT_TEST(WarnForUnusedSqlHint) {
+ NYql::TAstParseResult res = SqlToYql("select * from plato.Input1 as a join /*+ merge() */ plato.Input2 as b using(key);\n"
+ "select --+ foo(bar)\n"
+ " 1;");
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:23: Warning: Hint foo will not be used, code: 4534\n");
+ }
+
+ Y_UNIT_TEST(WarnForDeprecatedSchema) {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.ClusterMapping["s3bucket"] = NYql::S3ProviderName;
+ NYql::TAstParseResult res = SqlToYqlWithSettings("select * from s3bucket.`foo` with schema (col1 Int32, String as col2, Int64 as col3);", settings);
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "Warning: Deprecated syntax for positional schema: please use 'column type' instead of 'type AS column', code: 4535\n");
+ }
+
+ Y_UNIT_TEST(ErrorOnColumnNameInMaxByLimit) {
+ ExpectFailWithError(
+ "SELECT AGGREGATE_BY(AsTuple(value, key), AggregationFactory(\"MAX_BY\", subkey)) FROM plato.Input;",
+ "<main>:1:42: Error: Source does not allow column references\n"
+ "<main>:1:71: Error: Column reference 'subkey'\n");
+ }
+
+ Y_UNIT_TEST(ErrorInLibraryWithTopLevelNamedSubquery) {
+ TString withUnusedSubq = "$unused = select max(key) from plato.Input;\n"
+ "\n"
+ "define subquery $foo() as\n"
+ " $count = select count(*) from plato.Input;\n"
+ " select * from plato.Input limit $count / 2;\n"
+ "end define;\n"
+ "export $foo;\n";
+ UNIT_ASSERT(SqlToYqlWithMode(withUnusedSubq, NSQLTranslation::ESqlMode::LIBRARY).IsOk());
+
+ TString withTopLevelSubq = "$count = select count(*) from plato.Input;\n"
+ "\n"
+ "define subquery $foo() as\n"
+ " select * from plato.Input limit $count / 2;\n"
+ "end define;\n"
+ "export $foo;\n";
+ auto res = SqlToYqlWithMode(withTopLevelSubq, NSQLTranslation::ESqlMode::LIBRARY);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: Named subquery can not be used as a top level statement in libraries\n");
+ }
+
+ Y_UNIT_TEST(SessionStartAndSessionStateShouldSurviveSessionWindowArgsError){
+ TString query = R"(
+ $init = ($_row) -> (min(1, 2)); -- error: aggregation func min() can not be used here
+ $calculate = ($_row, $_state) -> (1);
+ $update = ($_row, $_state) -> (2);
+ SELECT
+ SessionStart() over w as session_start,
+ SessionState() over w as session_state,
+ FROM plato.Input as t
+ WINDOW w AS (
+ PARTITION BY user, SessionWindow(ts + 1, $init, $update, $calculate)
+ )
+ )";
+ ExpectFailWithError(query, "<main>:2:33: Error: Aggregation function Min requires exactly 1 argument(s), given: 2\n");
+ }
+
+ Y_UNIT_TEST(ScalarContextUsage1) {
+ TString query = R"(
+ $a = (select 1 as x, 2 as y);
+ select 1 + $a;
+ )";
+ ExpectFailWithError(query, "<main>:2:39: Error: Source used in expression should contain one concrete column\n"
+ "<main>:3:24: Error: Source is used here\n");
+ }
+
+ Y_UNIT_TEST(ScalarContextUsage2) {
+ TString query = R"(
+ use plato;
+ $a = (select 1 as x, 2 as y);
+ select * from concat($a);
+ )";
+ ExpectFailWithError(query, "<main>:3:39: Error: Source used in expression should contain one concrete column\n"
+ "<main>:4:34: Error: Source is used here\n");
+ }
+
+ Y_UNIT_TEST(ScalarContextUsage3) {
+ TString query = R"(
+ use plato;
+ $a = (select 1 as x, 2 as y);
+ select * from range($a);
+ )";
+ ExpectFailWithError(query, "<main>:3:39: Error: Source used in expression should contain one concrete column\n"
+ "<main>:4:33: Error: Source is used here\n");
+ }
+
+ Y_UNIT_TEST(ScalarContextUsage4) {
+ TString query = R"(
+ use plato;
+ $a = (select 1 as x, 2 as y);
+ insert into $a select 1;
+ )";
+ ExpectFailWithError(query, "<main>:3:39: Error: Source used in expression should contain one concrete column\n"
+ "<main>:4:25: Error: Source is used here\n");
+ }
+}
+
+void CheckUnused(const TString& req, const TString& symbol, unsigned row, unsigned col) {
+ auto res = SqlToYql(req);
+
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), TStringBuilder() << "<main>:" << row << ":" << col << ": Warning: Symbol " << symbol << " is not used, code: 4527\n");
+}
+
+Y_UNIT_TEST_SUITE(WarnUnused) {
+ Y_UNIT_TEST(ActionOrSubquery) {
+ TString req = " $a()\n"
+ "as select 1;\n"
+ "end define;\n"
+ "\n"
+ "select 1;";
+ CheckUnused("define action\n" + req, "$a", 2, 3);
+ CheckUnused("define subquery\n" + req, "$a", 2, 3);
+ }
+
+ Y_UNIT_TEST(Import) {
+ TString req = "import lib1 symbols\n"
+ " $sqr;\n"
+ "select 1;";
+ CheckUnused(req, "$sqr", 2, 3);
+
+ req = "import lib1 symbols\n"
+ " $sqr as\n"
+ " $sq;\n"
+ "select 1;";
+ CheckUnused(req, "$sq", 3, 5);
+ }
+
+ Y_UNIT_TEST(NamedNodeStatement) {
+ TString req = " $a, $a = AsTuple(1, 2);\n"
+ "select $a;";
+ CheckUnused(req, "$a", 1, 2);
+ req = "$a, $b = AsTuple(1, 2);\n"
+ "select $a;";
+ CheckUnused(req, "$b", 1, 6);
+ CheckUnused(" $a = 1; $a = 2; select $a;", "$a", 1, 2);
+ }
+
+ Y_UNIT_TEST(Declare) {
+ CheckUnused("declare $a as String;select 1;", "$a", 1, 9);
+ }
+
+ Y_UNIT_TEST(ActionParams) {
+ TString req = "define action $a($x, $y) as\n"
+ " select $x;\n"
+ "end define;\n"
+ "\n"
+ "do $a(1,2);";
+ CheckUnused(req, "$y", 1, 22);
+ }
+
+ Y_UNIT_TEST(SubqueryParams) {
+ TString req = "use plato;\n"
+ "define subquery $q($name, $x) as\n"
+ " select * from $name;\n"
+ "end define;\n"
+ "\n"
+ "select * from $q(\"Input\", 1);";
+ CheckUnused(req, "$x", 2, 27);
+ }
+
+ Y_UNIT_TEST(For) {
+ TString req = "define action $a() as\n"
+ " select 1;\n"
+ "end define;\n"
+ "\n"
+ "for $i in ListFromRange(1, 10)\n"
+ "do $a();";
+ CheckUnused(req, "$i", 5, 5);
+ }
+
+ Y_UNIT_TEST(LambdaParams) {
+ TString req = "$lambda = ($x, $y) -> ($x);\n"
+ "select $lambda(1, 2);";
+ CheckUnused(req, "$y", 1, 16);
+ }
+
+ Y_UNIT_TEST(InsideLambdaBody) {
+ TString req = "$lambda = () -> {\n"
+ " $x = 1; return 1;\n"
+ "};\n"
+ "select $lambda();";
+ CheckUnused(req, "$x", 2, 3);
+ req = "$lambda = () -> {\n"
+ " $x = 1; $x = 2; return $x;\n"
+ "};\n"
+ "select $lambda();";
+ CheckUnused(req, "$x", 2, 3);
+ }
+
+ Y_UNIT_TEST(InsideAction) {
+ TString req = "define action $a() as\n"
+ " $x = 1; select 1;\n"
+ "end define;\n"
+ "\n"
+ "do $a();";
+ CheckUnused(req, "$x", 2, 3);
+ req = "define action $a() as\n"
+ " $x = 1; $x = 2; select $x;\n"
+ "end define;\n"
+ "\n"
+ "do $a();";
+ CheckUnused(req, "$x", 2, 3);
+ }
+
+ Y_UNIT_TEST(NoWarnOnNestedActions) {
+ auto req = "pragma warning(\"error\", \"4527\");\n"
+ "define action $action($b) as\n"
+ " define action $aaa() as\n"
+ " select $b;\n"
+ " end define;\n"
+ " do $aaa();\n"
+ "end define;\n"
+ "\n"
+ "do $action(1);";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(NoWarnForUsageAfterSubquery) {
+ auto req = "use plato;\n"
+ "pragma warning(\"error\", \"4527\");\n"
+ "\n"
+ "$a = 1;\n"
+ "\n"
+ "define subquery $q($table) as\n"
+ " select * from $table;\n"
+ "end define;\n"
+ "\n"
+ "select * from $q(\"Input\");\n"
+ "select $a;";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+}
+
+Y_UNIT_TEST_SUITE(AnonymousNames) {
+ Y_UNIT_TEST(ReferenceAnonymousVariableIsForbidden) {
+ auto req = "$_ = 1; select $_;";
+
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:16: Error: Unable to reference anonymous name $_\n");
+
+ req = "$`_` = 1; select $`_`;";
+ res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:18: Error: Unable to reference anonymous name $_\n");
+ }
+
+ Y_UNIT_TEST(Declare) {
+ auto req = "declare $_ as String;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:9: Error: Can not use anonymous name '$_' in DECLARE statement\n");
+ }
+
+ Y_UNIT_TEST(ActionSubquery) {
+ auto req = "define action $_() as select 1; end define;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:15: Error: Can not use anonymous name '$_' as ACTION name\n");
+
+ req = "define subquery $_() as select 1; end define;";
+ res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: Can not use anonymous name '$_' as SUBQUERY name\n");
+ }
+
+ Y_UNIT_TEST(Import) {
+ auto req = "import lib symbols $sqr as $_;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:28: Error: Can not import anonymous name $_\n");
+ }
+
+ Y_UNIT_TEST(Export) {
+ auto req = "export $_;";
+ auto res = SqlToYqlWithMode(req, NSQLTranslation::ESqlMode::LIBRARY);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:8: Error: Can not export anonymous name $_\n");
+ }
+
+ Y_UNIT_TEST(AnonymousInActionArgs) {
+ auto req = "pragma warning(\"error\", \"4527\");\n"
+ "define action $a($_, $y, $_) as\n"
+ " select $y;\n"
+ "end define;\n"
+ "\n"
+ "do $a(1,2,3);";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(AnonymousInSubqueryArgs) {
+ auto req = "use plato;\n"
+ "pragma warning(\"error\", \"4527\");\n"
+ "define subquery $q($_, $y, $_) as\n"
+ " select * from $y;\n"
+ "end define;\n"
+ "\n"
+ "select * from $q(1,\"Input\",3);";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(AnonymousInLambdaArgs) {
+ auto req = "pragma warning(\"error\", \"4527\");\n"
+ "$lambda = ($_, $x, $_) -> ($x);\n"
+ "select $lambda(1,2,3);";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(AnonymousInFor) {
+ auto req = "pragma warning(\"error\", \"4527\");\n"
+ "evaluate for $_ in ListFromRange(1, 10) do begin select 1; end do;";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+
+ Y_UNIT_TEST(Assignment) {
+ auto req = "pragma warning(\"error\", \"4527\");\n"
+ "$_ = 1;\n"
+ "$_, $x, $_ = AsTuple(1,2,3);\n"
+ "select $x;";
+ UNIT_ASSERT(SqlToYql(req).IsOk());
+ }
+}
+
+Y_UNIT_TEST_SUITE(JsonValue) {
+ Y_UNIT_TEST(JsonValueArgumentCount) {
+ NYql::TAstParseResult res = SqlToYql("select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json));");
+
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Error: Unexpected token ')' : syntax error...\n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:49: Error: mismatched input ')' expecting ','\n");
+#endif
+ }
+
+ Y_UNIT_TEST(JsonValueJsonPathMustBeLiteralString) {
+ NYql::TAstParseResult res = SqlToYql("$jsonPath = \"strict $.key\"; select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), $jsonPath);");
+
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:79: Error: Unexpected token absence : Missing STRING_VALUE \n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:79: Error: mismatched input '$' expecting STRING_VALUE\n");
+#endif
+ }
+
+ Y_UNIT_TEST(JsonValueTranslation) {
+ NYql::TAstParseResult res = SqlToYql("select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\");");
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"strict $.key\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("SafeCast"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("DataType 'Json"));
+ };
+
+ TWordCountHive elementStat({"JsonValue"});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["JsonValue"]);
+ }
+
+ Y_UNIT_TEST(JsonValueReturningSection) {
+ for (const auto& typeName : {"Bool", "Int64", "Double", "String"}) {
+ NYql::TAstParseResult res = SqlToYql(
+ TStringBuilder() << "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" RETURNING " << typeName << ");"
+ );
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'\"strict $.key\""));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("SafeCast"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("DataType 'Json"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(TStringBuilder() << "DataType '" << typeName));
+ };
+
+ TWordCountHive elementStat({typeName});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT(elementStat[typeName] > 0);
+ }
+ }
+
+ Y_UNIT_TEST(JsonValueInvalidReturningType) {
+ NYql::TAstParseResult res = SqlToYql("select JSON_VALUE(CAST(@@{'key': 1238}@@ as Json), 'strict $.key' RETURNING invalid);");
+
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:77: Error: Unknown simple type 'invalid'\n");
+ }
+
+ Y_UNIT_TEST(JsonValueAndReturningInExpressions) {
+ NYql::TAstParseResult res = SqlToYql(
+ "USE plato\n;"
+ "$json_value = \"some string\";\n"
+ "SELECT $json_value;\n"
+ "SELECT 1 as json_value;\n"
+ "SELECT $json_value as json_value;\n"
+ "$returning = \"another string\";\n"
+ "SELECT $returning;\n"
+ "SELECT 1 as returning;\n"
+ "SELECT $returning as returning;\n"
+ );
+
+ UNIT_ASSERT(res.Root);
+ }
+
+ Y_UNIT_TEST(JsonValueValidCaseHandlers) {
+ const TVector<std::pair<TString, TString>> testCases = {
+ {"", "'DefaultValue (Null)"},
+ {"NULL", "'DefaultValue (Null)"},
+ {"ERROR", "'Error (Null)"},
+ {"DEFAULT 123", "'DefaultValue (Int32 '\"123\")"},
+ };
+
+ for (const auto& onEmpty : testCases) {
+ for (const auto& onError : testCases) {
+ TStringBuilder query;
+ query << "$json = CAST(@@{\"key\": 1238}@@ as Json);\n"
+ << "SELECT JSON_VALUE($json, \"strict $.key\"";
+ if (!onEmpty.first.empty()) {
+ query << " " << onEmpty.first << " ON EMPTY";
+ }
+ if (!onError.first.empty()) {
+ query << " " << onError.first << " ON ERROR";
+ }
+ query << ");\n";
+
+ NYql::TAstParseResult res = SqlToYql(query);
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(onEmpty.second + " " + onError.second));
+ };
+
+ TWordCountHive elementStat({"JsonValue"});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT(elementStat["JsonValue"] > 0);
+ }
+ }
+ }
+
+ Y_UNIT_TEST(JsonValueTooManyCaseHandlers) {
+ NYql::TAstParseResult res = SqlToYql(
+ "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" NULL ON EMPTY NULL ON ERROR NULL ON EMPTY);\n"
+ );
+
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(
+ Err2Str(res),
+ "<main>:1:52: Error: Only 1 ON EMPTY and/or 1 ON ERROR clause is expected\n"
+ );
+ }
+
+ Y_UNIT_TEST(JsonValueTooManyOnEmpty) {
+ NYql::TAstParseResult res = SqlToYql(
+ "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" NULL ON EMPTY NULL ON EMPTY);\n"
+ );
+
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(
+ Err2Str(res),
+ "<main>:1:52: Error: Only 1 ON EMPTY clause is expected\n"
+ );
+ }
+
+ Y_UNIT_TEST(JsonValueTooManyOnError) {
+ NYql::TAstParseResult res = SqlToYql(
+ "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" NULL ON ERROR NULL ON ERROR);\n"
+ );
+
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(
+ Err2Str(res),
+ "<main>:1:52: Error: Only 1 ON ERROR clause is expected\n"
+ );
+ }
+
+ Y_UNIT_TEST(JsonValueOnEmptyAfterOnError) {
+ NYql::TAstParseResult res = SqlToYql(
+ "select JSON_VALUE(CAST(@@{\"key\": 1238}@@ as Json), \"strict $.key\" NULL ON ERROR NULL ON EMPTY);\n"
+ );
+
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(
+ Err2Str(res),
+ "<main>:1:52: Error: ON EMPTY clause must be before ON ERROR clause\n"
+ );
+ }
+
+ Y_UNIT_TEST(JsonValueNullInput) {
+ NYql::TAstParseResult res = SqlToYql(R"(SELECT JSON_VALUE(NULL, "strict $.key");)");
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Nothing (OptionalType (DataType 'Json)))"));
+ };
+
+ TWordCountHive elementStat({"JsonValue"});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT(elementStat["JsonValue"] > 0);
+ }
+}
+
+Y_UNIT_TEST_SUITE(JsonExists) {
+ Y_UNIT_TEST(JsonExistsValidHandlers) {
+ const TVector<std::pair<TString, TString>> testCases = {
+ {"", "(Just (Bool '\"false\"))"},
+ {"TRUE ON ERROR", "(Just (Bool '\"true\"))"},
+ {"FALSE ON ERROR", "(Just (Bool '\"false\"))"},
+ {"UNKNOWN ON ERROR", "(Nothing (OptionalType (DataType 'Bool)))"},
+ // NOTE: in this case we expect arguments of JsonExists callable to end immediately
+ // after variables. This parenthesis at the end of the expression is left on purpose
+ {"ERROR ON ERROR", "(Utf8 '\"strict $.key\") (JsonVariables))"},
+ };
+
+ for (const auto& item : testCases) {
+ NYql::TAstParseResult res = SqlToYql(
+ TStringBuilder() << R"(
+ $json = CAST(@@{"key": 1238}@@ as Json);
+ SELECT JSON_EXISTS($json, "strict $.key" )" << item.first << ");\n"
+ );
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(item.second));
+ };
+
+ TWordCountHive elementStat({"JsonExists"});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT(elementStat["JsonExists"] > 0);
+ }
+ }
+
+ Y_UNIT_TEST(JsonExistsInvalidHandler) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ $json = CAST(@@{"key": 1238}@@ as Json);
+ $default = false;
+ SELECT JSON_EXISTS($json, "strict $.key" $default ON ERROR);
+ )");
+
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:53: Error: Unexpected token absence : Missing RPAREN \n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:53: Error: mismatched input '$' expecting {')', ERROR, FALSE, TRUE, UNKNOWN}\n");
+#endif
+ }
+
+ Y_UNIT_TEST(JsonExistsNullInput) {
+ NYql::TAstParseResult res = SqlToYql(R"(SELECT JSON_EXISTS(NULL, "strict $.key");)");
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Nothing (OptionalType (DataType 'Json)))"));
+ };
+
+ TWordCountHive elementStat({"JsonExists"});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT(elementStat["JsonExists"] > 0);
+ }
+}
+
+Y_UNIT_TEST_SUITE(JsonQuery) {
+ Y_UNIT_TEST(JsonQueryValidHandlers) {
+ using TTestSuite = const TVector<std::pair<TString, TString>>;
+ TTestSuite wrapCases = {
+ {"", "'NoWrap"},
+ {"WITHOUT WRAPPER", "'NoWrap"},
+ {"WITHOUT ARRAY WRAPPER", "'NoWrap"},
+ {"WITH WRAPPER", "'Wrap"},
+ {"WITH ARRAY WRAPPER", "'Wrap"},
+ {"WITH UNCONDITIONAL WRAPPER", "'Wrap"},
+ {"WITH UNCONDITIONAL ARRAY WRAPPER", "'Wrap"},
+ {"WITH CONDITIONAL WRAPPER", "'ConditionalWrap"},
+ {"WITH CONDITIONAL ARRAY WRAPPER", "'ConditionalWrap"},
+ };
+ TTestSuite handlerCases = {
+ {"", "'Null"},
+ {"ERROR", "'Error"},
+ {"NULL", "'Null"},
+ {"EMPTY ARRAY", "'EmptyArray"},
+ {"EMPTY OBJECT", "'EmptyObject"},
+ };
+
+ for (const auto& wrap : wrapCases) {
+ for (const auto& onError : handlerCases) {
+ for (const auto& onEmpty : handlerCases) {
+ TStringBuilder query;
+ query << R"($json = CAST(@@{"key": [123]}@@ as Json);
+ SELECT JSON_QUERY($json, "strict $.key" )" << wrap.first;
+ if (!onEmpty.first.empty()) {
+ if (wrap.first.StartsWith("WITH ")) {
+ continue;
+ }
+ query << " " << onEmpty.first << " ON EMPTY";
+ }
+ if (!onError.first.empty()) {
+ query << " " << onError.first << " ON ERROR";
+ }
+ query << ");\n";
+
+ NYql::TAstParseResult res = SqlToYql(query);
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ const TString args = TStringBuilder() << wrap.second << " " << onEmpty.second << " " << onError.second;
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(args));
+ };
+
+ Cout << wrap.first << " " << onEmpty.first << " " << onError.first << Endl;
+
+ TWordCountHive elementStat({"JsonQuery"});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT(elementStat["JsonQuery"] > 0);
+ }
+ }
+ }
+ }
+
+ Y_UNIT_TEST(JsonQueryOnEmptyWithWrapper) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ $json = CAST(@@{"key": 1238}@@ as Json);
+ SELECT JSON_QUERY($json, "strict $" WITH ARRAY WRAPPER EMPTY ARRAY ON EMPTY);
+ )");
+
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:38: Error: ON EMPTY is prohibited because WRAPPER clause is specified\n");
+ }
+
+ Y_UNIT_TEST(JsonQueryNullInput) {
+ NYql::TAstParseResult res = SqlToYql(R"(SELECT JSON_QUERY(NULL, "strict $.key");)");
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("(Nothing (OptionalType (DataType 'Json)))"));
+ };
+
+ TWordCountHive elementStat({"JsonQuery"});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT(elementStat["JsonQuery"] > 0);
+ }
+}
+
+Y_UNIT_TEST_SUITE(JsonPassing) {
+ Y_UNIT_TEST(SupportedVariableTypes) {
+ const TVector<TString> functions = {"JSON_EXISTS", "JSON_VALUE", "JSON_QUERY"};
+
+ for (const auto& function : functions) {
+ const auto query = Sprintf(R"(
+ pragma CompactNamedExprs;
+ $json = CAST(@@{"key": 1238}@@ as Json);
+ SELECT %s(
+ $json,
+ "strict $.key"
+ PASSING
+ "string" as var1,
+ 1.234 as var2,
+ CAST(1 as Int64) as var3,
+ true as var4,
+ $json as var5
+ ))",
+ function.data()
+ );
+ NYql::TAstParseResult res = SqlToYql(query);
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var1" (String '"string")))"), "Cannot find `var1`");
+ UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var2" (Double '"1.234")))"), "Cannot find `var2`");
+ UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var3" (SafeCast (Int32 '"1") (DataType 'Int64))))"), "Cannot find `var3`");
+ UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var4" (Bool '"true")))"), "Cannot find `var4`");
+ UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var5" namedexprnode0))"), "Cannot find `var5`");
+ };
+
+ TWordCountHive elementStat({"JsonVariables"});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT(elementStat["JsonVariables"] > 0);
+ }
+ }
+
+ Y_UNIT_TEST(ValidVariableNames) {
+ const TVector<TString> functions = {"JSON_EXISTS", "JSON_VALUE", "JSON_QUERY"};
+
+ for (const auto& function : functions) {
+ const auto query = Sprintf(R"(
+ $json = CAST(@@{"key": 1238}@@ as Json);
+ SELECT %s(
+ $json,
+ "strict $.key"
+ PASSING
+ "one" as var1,
+ "two" as "VaR2",
+ "three" as `var3`,
+ "four" as VaR4
+ ))",
+ function.data()
+ );
+ NYql::TAstParseResult res = SqlToYql(query);
+
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var1" (String '"one")))"), "Cannot find `var1`");
+ UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"VaR2" (String '"two")))"), "Cannot find `VaR2`");
+ UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"var3" (String '"three")))"), "Cannot find `var3`");
+ UNIT_ASSERT_VALUES_UNEQUAL_C(TString::npos, line.find(R"('('"VaR4" (String '"four")))"), "Cannot find `VaR4`");
+ };
+
+ TWordCountHive elementStat({"JsonVariables"});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT(elementStat["JsonVariables"] > 0);
+ }
+ }
+}
+
+Y_UNIT_TEST_SUITE(MigrationToJsonApi) {
+ Y_UNIT_TEST(WarningOnDeprecatedJsonUdf) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ $json = CAST(@@{"key": 1234}@@ as Json);
+ SELECT Json::Parse($json);
+ )");
+
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:26: Warning: Json UDF is deprecated. Please use JSON API instead, code: 4506\n");
+ }
+}
+
+Y_UNIT_TEST_SUITE(AnsiIdentsNegative) {
+ Y_UNIT_TEST(EnableAnsiLexerFromRequestSpecialComments) {
+ auto req = "\n"
+ "\t --!ansi_lexer \n"
+ "-- Some comment\n"
+ "-- another comment\n"
+ "pragma SimpleColumns;\n"
+ "\n"
+ "select 1, '''' as empty;";
+
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+ }
+
+ Y_UNIT_TEST(AnsiLexerShouldNotBeEnabledHere) {
+ auto req = "$str = '\n"
+ "--!ansi_lexer\n"
+ "--!syntax_v1\n"
+ "';\n"
+ "\n"
+ "select 1, $str, \"\" as empty;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+ }
+
+ Y_UNIT_TEST(DoubleQuotesInDictsTuplesOrLists) {
+ auto req = "$d = { 'a': 1, \"b\": 2, 'c': 3,};";
+
+ auto res = SqlToYqlWithAnsiLexer(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:16: Error: Column reference \"b\" is not allowed in current scope\n");
+
+ req = "$t = (1, 2, \"a\");";
+
+ res = SqlToYqlWithAnsiLexer(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:13: Error: Column reference \"a\" is not allowed in current scope\n");
+
+ req = "$l = ['a', 'b', \"c\"];";
+
+ res = SqlToYqlWithAnsiLexer(req);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: Column reference \"c\" is not allowed in current scope\n");
+ }
+
+ Y_UNIT_TEST(MultilineComments) {
+ auto req = "/*/**/ select 1;";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ res = SqlToYqlWithAnsiLexer(req);
+#if ANTLR_VER == 3
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:16: Error: Unexpected character : syntax error...\n\n");
+#else
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+#endif
+ req = "/*\n"
+ "--/*\n"
+ "*/ select 1;";
+ res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ res = SqlToYqlWithAnsiLexer(req);
+#if ANTLR_VER == 3
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:3:12: Error: Unexpected character : syntax error...\n\n");
+#else
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+#endif
+ req = "/*\n"
+ "/*\n"
+ "--*/\n"
+ "*/ select 1;";
+ res = SqlToYql(req);
+ UNIT_ASSERT(!res.Root);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:0: Error: Unexpected token '*' : cannot match to any predicted input...\n\n");
+#else
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:0: Error: mismatched input '*' expecting {';', '(', '$', ALTER, ANALYZE, BACKUP, BATCH, COMMIT, CREATE, DECLARE, DEFINE, DELETE, DISCARD, DO, DROP, EVALUATE, EXPLAIN, EXPORT, FOR, FROM, GRANT, IF, IMPORT, INSERT, PARALLEL, PRAGMA, PROCESS, REDUCE, REPLACE, RESTORE, REVOKE, ROLLBACK, SELECT, SHOW, UPDATE, UPSERT, USE, VALUES}\n");
+#endif
+ res = SqlToYqlWithAnsiLexer(req);
+ UNIT_ASSERT(res.Root);
+ }
+}
+
+Y_UNIT_TEST_SUITE(AnsiOptionalAs) {
+ Y_UNIT_TEST(OptionalAsInProjection) {
+ UNIT_ASSERT(SqlToYql("PRAGMA AnsiOptionalAs; SELECT a b, c FROM plato.Input;").IsOk());
+ ExpectFailWithError("PRAGMA DisableAnsiOptionalAs;\n"
+ "SELECT a b, c FROM plato.Input;",
+ "<main>:2:10: Error: Expecting mandatory AS here. Did you miss comma? Please add PRAGMA AnsiOptionalAs; for ANSI compatibility\n");
+ }
+
+ Y_UNIT_TEST(OptionalAsWithKeywords) {
+ UNIT_ASSERT(SqlToYql("PRAGMA AnsiOptionalAs; SELECT a type, b data, c source FROM plato.Input;").IsOk());
+ }
+}
+
+Y_UNIT_TEST_SUITE(SessionWindowNegative) {
+ Y_UNIT_TEST(SessionWindowWithoutSource) {
+ ExpectFailWithError("SELECT 1 + SessionWindow(ts, 32);",
+ "<main>:1:12: Error: SessionWindow requires data source\n");
+ }
+
+ Y_UNIT_TEST(SessionWindowInProjection) {
+ ExpectFailWithError("SELECT 1 + SessionWindow(ts, 32) from plato.Input;",
+ "<main>:1:12: Error: SessionWindow can only be used as a top-level GROUP BY / PARTITION BY expression\n");
+ }
+
+ Y_UNIT_TEST(SessionWindowWithNonConstSecondArg) {
+ ExpectFailWithError(
+ "SELECT key, session_start FROM plato.Input\n"
+ "GROUP BY SessionWindow(ts, 32 + subkey) as session_start, key;",
+
+ "<main>:2:10: Error: Source does not allow column references\n"
+ "<main>:2:33: Error: Column reference 'subkey'\n");
+ }
+
+ Y_UNIT_TEST(SessionWindowWithWrongNumberOfArgs) {
+ ExpectFailWithError("SELECT * FROM plato.Input GROUP BY SessionWindow()",
+ "<main>:1:36: Error: SessionWindow requires either two or four arguments\n");
+ ExpectFailWithError("SELECT * FROM plato.Input GROUP BY SessionWindow(key, subkey, 100)",
+ "<main>:1:36: Error: SessionWindow requires either two or four arguments\n");
+ }
+
+ Y_UNIT_TEST(DuplicateSessionWindow) {
+ ExpectFailWithError(
+ "SELECT\n"
+ " *\n"
+ "FROM plato.Input\n"
+ "GROUP BY\n"
+ " SessionWindow(ts, 10),\n"
+ " user,\n"
+ " SessionWindow(ts, 20)\n"
+ ";",
+
+ "<main>:7:5: Error: Duplicate session window specification:\n"
+ "<main>:5:5: Error: Previous session window is declared here\n");
+
+ ExpectFailWithError(
+ "SELECT\n"
+ " MIN(key) over w\n"
+ "FROM plato.Input\n"
+ "WINDOW w AS (\n"
+ " PARTITION BY SessionWindow(ts, 10), user,\n"
+ " SessionWindow(ts, 20)\n"
+ ");",
+
+ "<main>:6:5: Error: Duplicate session window specification:\n"
+ "<main>:5:18: Error: Previous session window is declared here\n");
+ }
+
+ Y_UNIT_TEST(SessionStartStateWithoutSource) {
+ ExpectFailWithError("SELECT 1 + SessionStart();",
+ "<main>:1:12: Error: SessionStart requires data source\n");
+ ExpectFailWithError("SELECT 1 + SessionState();",
+ "<main>:1:12: Error: SessionState requires data source\n");
+ }
+
+ Y_UNIT_TEST(SessionStartStateWithoutGroupByOrWindow) {
+ ExpectFailWithError("SELECT 1 + SessionStart() from plato.Input;",
+ "<main>:1:12: Error: SessionStart can not be used without aggregation by SessionWindow\n");
+ ExpectFailWithError("SELECT 1 + SessionState() from plato.Input;",
+ "<main>:1:12: Error: SessionState can not be used without aggregation by SessionWindow\n");
+ }
+
+ Y_UNIT_TEST(SessionStartStateWithGroupByWithoutSession) {
+ ExpectFailWithError("SELECT 1 + SessionStart() from plato.Input group by user;",
+ "<main>:1:12: Error: SessionStart can not be used here: SessionWindow specification is missing in GROUP BY\n");
+ ExpectFailWithError("SELECT 1 + SessionState() from plato.Input group by user;",
+ "<main>:1:12: Error: SessionState can not be used here: SessionWindow specification is missing in GROUP BY\n");
+ }
+
+ Y_UNIT_TEST(SessionStartStateWithoutOverWithWindowWithoutSession) {
+ ExpectFailWithError("SELECT 1 + SessionStart(), MIN(key) over w from plato.Input window w as ()",
+ "<main>:1:12: Error: SessionStart can not be used without aggregation by SessionWindow. Maybe you forgot to add OVER `window_name`?\n");
+ ExpectFailWithError("SELECT 1 + SessionState(), MIN(key) over w from plato.Input window w as ()",
+ "<main>:1:12: Error: SessionState can not be used without aggregation by SessionWindow. Maybe you forgot to add OVER `window_name`?\n");
+ }
+
+ Y_UNIT_TEST(SessionStartStateWithWindowWithoutSession) {
+ ExpectFailWithError("SELECT 1 + SessionStart() over w, MIN(key) over w from plato.Input window w as ()",
+ "<main>:1:12: Error: SessionStart can not be used with window w: SessionWindow specification is missing in PARTITION BY\n");
+ ExpectFailWithError("SELECT 1 + SessionState() over w, MIN(key) over w from plato.Input window w as ()",
+ "<main>:1:12: Error: SessionState can not be used with window w: SessionWindow specification is missing in PARTITION BY\n");
+ }
+
+ Y_UNIT_TEST(SessionStartStateWithSessionedWindow) {
+ ExpectFailWithError("SELECT 1 + SessionStart(), MIN(key) over w from plato.Input group by key window w as (partition by SessionWindow(ts, 1)) ",
+ "<main>:1:12: Error: SessionStart can not be used here: SessionWindow specification is missing in GROUP BY. Maybe you forgot to add OVER `window_name`?\n");
+ ExpectFailWithError("SELECT 1 + SessionState(), MIN(key) over w from plato.Input group by key window w as (partition by SessionWindow(ts, 1)) ",
+ "<main>:1:12: Error: SessionState can not be used here: SessionWindow specification is missing in GROUP BY. Maybe you forgot to add OVER `window_name`?\n");
+ }
+
+ Y_UNIT_TEST(AggregationBySessionStateIsNotSupportedYet) {
+ ExpectFailWithError("SELECT SOME(1 + SessionState()), key from plato.Input group by key, SessionWindow(ts, 1);",
+ "<main>:1:17: Error: SessionState with GROUP BY is not supported yet\n");
+ }
+
+ Y_UNIT_TEST(SessionWindowInRtmr) {
+ NYql::TAstParseResult res = SqlToYql(
+ "SELECT * FROM plato.Input GROUP BY SessionWindow(ts, 10);",
+ 10, TString(NYql::RtmrProviderName));
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:54: Error: Streaming group by query must have a hopping window specification.\n");
+
+ res = SqlToYql(R"(
+ SELECT key, SUM(value) AS value FROM plato.Input
+ GROUP BY key, HOP(subkey, "PT10S", "PT30S", "PT20S"), SessionWindow(ts, 10);
+ )", 10, TString(NYql::RtmrProviderName));
+
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:13: Error: SessionWindow is unsupported for streaming sources\n");
+ }
+}
+
+Y_UNIT_TEST_SUITE(LibraSqlSugar) {
+ auto makeResult = [](TStringBuf settings) {
+ return SqlToYql(
+ TStringBuilder()
+ << settings
+ << "\n$udf1 = MyLibra::MakeLibraPreprocessor($settings);"
+ << "\n$udf2 = CustomLibra::MakeLibraPreprocessor($settings);"
+ << "\nPROCESS plato.Input USING $udf1(TableRow())"
+ << "\nUNION ALL"
+ << "\nPROCESS plato.Input USING $udf2(TableRow());"
+ );
+ };
+
+ Y_UNIT_TEST(EmptySettings) {
+ auto res = makeResult(R"(
+ $settings = AsStruct();
+ )");
+ UNIT_ASSERT(res.IsOk());
+ }
+
+ Y_UNIT_TEST(OnlyEntities) {
+ auto res = makeResult(R"(
+ $settings = AsStruct(
+ AsList("A", "B", "C") AS Entities
+ );
+ )");
+ UNIT_ASSERT(res.IsOk());
+ }
+
+ Y_UNIT_TEST(EntitiesWithStrategy) {
+ auto res = makeResult(R"(
+ $settings = AsStruct(
+ AsList("A", "B", "C") AS Entities,
+ "blacklist" AS EntitiesStrategy
+ );
+ )");
+ UNIT_ASSERT(res.IsOk());
+ }
+
+ Y_UNIT_TEST(AllSettings) {
+ auto res = makeResult(R"(
+ $settings = AsStruct(
+ AsList("A", "B", "C") AS Entities,
+ "whitelist" AS EntitiesStrategy,
+ "path" AS BlockstatDict,
+ false AS ParseWithFat,
+ "map" AS Mode
+ );
+ )");
+ UNIT_ASSERT(res.IsOk());
+ }
+
+ Y_UNIT_TEST(BadStrategy) {
+ auto res = makeResult(R"(
+ $settings = AsStruct("bad" AS EntitiesStrategy);
+ )");
+ UNIT_ASSERT_STRING_CONTAINS(
+ Err2Str(res),
+ "Error: MakeLibraPreprocessor got invalid entities strategy: expected 'whitelist' or 'blacklist'"
+ );
+ }
+
+ Y_UNIT_TEST(BadEntities) {
+ auto res = makeResult(R"(
+ $settings = AsStruct(AsList("A", 1) AS Entities);
+ )");
+ UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "Error: MakeLibraPreprocessor entity must be string literal");
+ }
+}
+
+Y_UNIT_TEST_SUITE(TrailingQuestionsNegative) {
+ Y_UNIT_TEST(Basic) {
+ ExpectFailWithError("SELECT 1?;", "<main>:1:9: Error: Unexpected token '?' at the end of expression\n");
+#if ANTLR_VER == 3
+ ExpectFailWithError("SELECT 1? + 1;", "<main>:1:10: Error: Unexpected token '+' : cannot match to any predicted input...\n\n");
+#else
+ ExpectFailWithError("SELECT 1? + 1;", "<main>:1:10: Error: mismatched input '+' expecting {<EOF>, ';'}\n");
+#endif
+ ExpectFailWithError("SELECT 1 + 1??? < 2", "<main>:1:13: Error: Unexpected token '?' at the end of expression\n");
+ ExpectFailWithError("SELECT 1? > 2? > 3?",
+ "<main>:1:11: Error: Unexpected token '?' at the end of expression\n"
+ "<main>:1:16: Error: Unexpected token '?' at the end of expression\n"
+ "<main>:1:21: Error: Unexpected token '?' at the end of expression\n");
+ }
+
+ Y_UNIT_TEST(SmartParen) {
+ ExpectFailWithError("$x = 1; SELECT (Int32?, $x?)", "<main>:1:27: Error: Unexpected token '?' at the end of expression\n");
+ ExpectFailWithError("SELECT (Int32, foo?)", "<main>:1:19: Error: Unexpected token '?' at the end of expression\n");
+ }
+
+ Y_UNIT_TEST(LambdaOptArgs) {
+ ExpectFailWithError("$l = ($x, $y?, $z??, $t?) -> ($x);", "<main>:1:18: Error: Expecting at most one '?' token here (for optional lambda parameters), but got 2\n");
+ }
+}
+
+Y_UNIT_TEST_SUITE(FlexibleTypes) {
+ Y_UNIT_TEST(AssumeOrderByType) {
+ UNIT_ASSERT(SqlToYql("PRAGMA FlexibleTypes; SELECT 1 AS int32 ASSUME ORDER BY int32").IsOk());
+ }
+
+ Y_UNIT_TEST(GroupingSets) {
+ UNIT_ASSERT(SqlToYql("PRAGMA FlexibleTypes; SELECT COUNT(*) AS cnt, text, uuid FROM plato.Input GROUP BY GROUPING SETS((uuid), (uuid, text));").IsOk());
+ }
+
+ Y_UNIT_TEST(WeakField) {
+ UNIT_ASSERT(SqlToYql("PRAGMA FlexibleTypes; SELECT WeakField(text, string) as text FROM plato.Input").IsOk());
+ }
+
+ Y_UNIT_TEST(Aggregation1) {
+ TString q =
+ "PRAGMA FlexibleTypes;\n"
+ "$foo = ($x, $const, $type) -> ($x || $const || FormatType($type));\n"
+ "SELECT $foo(SOME(x), 'aaa', String) FROM plato.Input GROUP BY y;";
+ UNIT_ASSERT(SqlToYql(q).IsOk());
+ }
+
+ Y_UNIT_TEST(Aggregation2) {
+ TString q =
+ "PRAGMA FlexibleTypes;\n"
+ "SELECT 1 + String + MAX(key) FROM plato.Input;";
+ UNIT_ASSERT(SqlToYql(q).IsOk());
+ }
+}
+
+Y_UNIT_TEST_SUITE(ExternalDeclares) {
+ Y_UNIT_TEST(BasicUsage) {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.DeclaredNamedExprs["foo"] = "String";
+ auto res = SqlToYqlWithSettings("select $foo;", settings);
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "declare") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"__((declare "$foo" (DataType 'String)))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("declare"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["declare"]);
+ }
+
+ Y_UNIT_TEST(DeclareOverrides) {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.DeclaredNamedExprs["foo"] = "String";
+ auto res = SqlToYqlWithSettings("declare $foo as Int32; select $foo;", settings);
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "declare") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"__((declare "$foo" (DataType 'Int32)))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("declare"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["declare"]);
+ }
+
+ Y_UNIT_TEST(UnusedDeclareDoesNotProduceWarning) {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.DeclaredNamedExprs["foo"] = "String";
+ auto res = SqlToYqlWithSettings("select 1;", settings);
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "declare") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"__((declare "$foo" (DataType 'String)))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("declare"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["declare"]);
+ }
+
+ Y_UNIT_TEST(DeclaresWithInvalidTypesFails) {
+ NSQLTranslation::TTranslationSettings settings;
+ settings.DeclaredNamedExprs["foo"] = "List<BadType>";
+ auto res = SqlToYqlWithSettings("select 1;", settings);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res),
+ "<main>:0:5: Error: Unknown type: 'BadType'\n"
+ "<main>: Error: Failed to parse type for externally declared name 'foo'\n");
+ }
+}
+
+Y_UNIT_TEST_SUITE(ExternalDataSource) {
+ Y_UNIT_TEST(CreateExternalDataSourceWithAuthNone) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="ObjectStorage",
+ LOCATION="my-bucket",
+ AUTH_METHOD="NONE"
+ );
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"NONE") '('"location" '"my-bucket") '('"source_type" '"ObjectStorage"))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateExternalDataSourceWithAuthServiceAccount) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="ObjectStorage",
+ LOCATION="my-bucket",
+ AUTH_METHOD="SERVICE_ACCOUNT",
+ SERVICE_ACCOUNT_ID="sa",
+ SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name"
+ );
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"SERVICE_ACCOUNT") '('"location" '"my-bucket") '('"service_account_id" '"sa") '('"service_account_secret_name" '"sa_secret_name") '('"source_type" '"ObjectStorage"))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateExternalDataSourceWithBasic) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="PostgreSQL",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="BASIC",
+ LOGIN="admin",
+ PASSWORD_SECRET_NAME="secret_name"
+ );
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"BASIC") '('"location" '"protocol://host:port/") '('"login" '"admin") '('"password_secret_name" '"secret_name") '('"source_type" '"PostgreSQL"))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateExternalDataSourceWithMdbBasic) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="PostgreSQL",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="MDB_BASIC",
+ SERVICE_ACCOUNT_ID="sa",
+ SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name",
+ LOGIN="admin",
+ PASSWORD_SECRET_NAME="secret_name"
+ );
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"MDB_BASIC") '('"location" '"protocol://host:port/") '('"login" '"admin") '('"password_secret_name" '"secret_name") '('"service_account_id" '"sa") '('"service_account_secret_name" '"sa_secret_name") '('"source_type" '"PostgreSQL"))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateExternalDataSourceWithAws) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="PostgreSQL",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="AWS",
+ AWS_ACCESS_KEY_ID_SECRET_NAME="secred_id_name",
+ AWS_SECRET_ACCESS_KEY_SECRET_NAME="secret_key_name",
+ AWS_REGION="ru-central-1"
+ );
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"AWS") '('"aws_access_key_id_secret_name" '"secred_id_name") '('"aws_region" '"ru-central-1") '('"aws_secret_access_key_secret_name" '"secret_key_name") '('"location" '"protocol://host:port/") '('"source_type" '"PostgreSQL"))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateExternalDataSourceWithToken) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="YT",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="TOKEN",
+ TOKEN_SECRET_NAME="token_name"
+ );
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"TOKEN") '('"location" '"protocol://host:port/") '('"source_type" '"YT") '('"token_secret_name" '"token_name"))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateExternalDataSourceWithTablePrefix) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ pragma TablePathPrefix='/aba';
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="ObjectStorage",
+ LOCATION="my-bucket",
+ AUTH_METHOD="NONE"
+ );
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, "/aba/MyDataSource");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateExternalDataSourceIfNotExists) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE IF NOT EXISTS MyDataSource WITH (
+ SOURCE_TYPE="ObjectStorage",
+ LOCATION="my-bucket",
+ AUTH_METHOD="NONE"
+ );
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"NONE") '('"location" '"my-bucket") '('"source_type" '"ObjectStorage"))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObjectIfNotExists"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(AlterExternalDataSource) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ ALTER EXTERNAL DATA SOURCE MyDataSource
+ SET (SOURCE_TYPE = "ObjectStorage", Login = "Admin"),
+ SET Location "bucket",
+ RESET (Auth_Method, Service_Account_Id, Service_Account_Secret_Name);
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alterObject))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('features '('('"location" '"bucket") '('"login" '"Admin") '('"source_type" '"ObjectStorage"))))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('resetFeatures '('"auth_method" '"service_account_id" '"service_account_secret_name")))#");
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateExternalDataSourceOrReplace) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+ CREATE OR REPLACE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="ObjectStorage",
+ LOCATION="my-bucket",
+ AUTH_METHOD="NONE"
+ );
+ )");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"NONE") '('"location" '"my-bucket") '('"source_type" '"ObjectStorage"))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObjectOrReplace"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateOrReplaceForUnsupportedTableTypesShouldFail) {
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE OR REPLACE TABLE t (a int32 not null, primary key(a, a));
+ )sql" , "<main>:3:23: Error: OR REPLACE feature is supported only for EXTERNAL DATA SOURCE and EXTERNAL TABLE\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE OR REPLACE TABLE t (
+ Key Uint64,
+ Value1 String,
+ PRIMARY KEY (Key)
+ )
+ WITH (
+ STORE = COLUMN,
+ AUTO_PARTITIONING_MIN_PARTITIONS_COUNT = 10
+ );
+ )sql" , "<main>:3:23: Error: OR REPLACE feature is supported only for EXTERNAL DATA SOURCE and EXTERNAL TABLE\n");
+ }
+
+ Y_UNIT_TEST(CreateExternalDataSourceWithBadArguments) {
+#if ANTLR_VER == 3
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource;
+ )sql" , "<main>:3:52: Error: Unexpected token ';' : syntax error...\n\n");
+#else
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource;
+ )sql" , "<main>:3:52: Error: mismatched input ';' expecting WITH\n");
+#endif
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ LOCATION="my-bucket",
+ AUTH_METHOD="NONE"
+ );
+ )sql" , "<main>:5:33: Error: SOURCE_TYPE requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="ObjectStorage",
+ LOCATION="my-bucket"
+ );
+ )sql" , "<main>:5:30: Error: AUTH_METHOD requires key\n");
+
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="ObjectStorage",
+ LOCATION="my-bucket",
+ AUTH_METHOD="NONE1"
+ );
+ )sql" , "<main>:6:33: Error: Unknown AUTH_METHOD = NONE1\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="ObjectStorage",
+ LOCATION="my-bucket",
+ AUTH_METHOD="SERVICE_ACCOUNT"
+ );
+ )sql" , "<main>:6:33: Error: SERVICE_ACCOUNT_ID requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="ObjectStorage",
+ LOCATION="my-bucket",
+ AUTH_METHOD="SERVICE_ACCOUNT",
+ SERVICE_ACCOUNT_ID="s1"
+ );
+ )sql" , "<main>:7:40: Error: SERVICE_ACCOUNT_SECRET_NAME requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="ObjectStorage",
+ LOCATION="my-bucket",
+ AUTH_METHOD="SERVICE_ACCOUNT",
+ SERVICE_ACCOUNT_SECRET_NAME="s1"
+ );
+ )sql" , "<main>:7:49: Error: SERVICE_ACCOUNT_ID requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="PostgreSQL",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="BASIC",
+ LOGIN="admin"
+ );
+ )sql" , "<main>:7:27: Error: PASSWORD_SECRET_NAME requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="PostgreSQL",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="BASIC",
+ PASSWORD_SECRET_NAME="secret_name"
+ );
+ )sql" , "<main>:7:42: Error: LOGIN requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="PostgreSQL",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="MDB_BASIC",
+ SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name",
+ LOGIN="admin",
+ PASSWORD_SECRET_NAME="secret_name"
+ );
+ )sql" , "<main>:9:42: Error: SERVICE_ACCOUNT_ID requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="PostgreSQL",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="MDB_BASIC",
+ SERVICE_ACCOUNT_ID="sa",
+ LOGIN="admin",
+ PASSWORD_SECRET_NAME="secret_name"
+ );
+ )sql" , "<main>:9:42: Error: SERVICE_ACCOUNT_SECRET_NAME requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="PostgreSQL",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="MDB_BASIC",
+ SERVICE_ACCOUNT_ID="sa",
+ SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name",
+ PASSWORD_SECRET_NAME="secret_name"
+ );
+ )sql" , "<main>:9:42: Error: LOGIN requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="PostgreSQL",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="MDB_BASIC",
+ SERVICE_ACCOUNT_ID="sa",
+ SERVICE_ACCOUNT_SECRET_NAME="sa_secret_name",
+ LOGIN="admin"
+ );
+ )sql" , "<main>:9:27: Error: PASSWORD_SECRET_NAME requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="PostgreSQL",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="AWS",
+ AWS_SECRET_ACCESS_KEY_SECRET_NAME="secret_key_name",
+ AWS_REGION="ru-central-1"
+ );
+ )sql" , "<main>:8:32: Error: AWS_ACCESS_KEY_ID_SECRET_NAME requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="PostgreSQL",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="AWS",
+ AWS_ACCESS_KEY_ID_SECRET_NAME="secred_id_name",
+ AWS_REGION="ru-central-1"
+ );
+ )sql" , "<main>:8:32: Error: AWS_SECRET_ACCESS_KEY_SECRET_NAME requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL DATA SOURCE MyDataSource WITH (
+ SOURCE_TYPE="PostgreSQL",
+ LOCATION="protocol://host:port/",
+ AUTH_METHOD="AWS",
+ AWS_SECRET_ACCESS_KEY_SECRET_NAME="secret_key_name",
+ AWS_ACCESS_KEY_ID_SECRET_NAME="secred_id_name"
+ );
+ )sql" , "<main>:8:51: Error: AWS_REGION requires key\n");
+ }
+
+ Y_UNIT_TEST(DropExternalDataSourceWithTablePrefix) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ DROP EXTERNAL DATA SOURCE MyDataSource;
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DropExternalDataSourceIfExists) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ DROP EXTERNAL DATA SOURCE IF EXISTS MyDataSource;
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, "MyDataSource");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObjectIfExists"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DropExternalDataSource) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ pragma TablePathPrefix='/aba';
+ DROP EXTERNAL DATA SOURCE MyDataSource;
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, "/aba/MyDataSource");
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+}
+
+Y_UNIT_TEST_SUITE(ExternalTable) {
+ Y_UNIT_TEST(CreateExternalTable) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE EXTERNAL TABLE mytable (
+ a int
+ ) WITH (
+ DATA_SOURCE="/Root/mydatasource",
+ LOCATION="/folder1/*"
+ );
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('data_source_path (String '"/Root/mydatasource")) '('location (String '"/folder1/*")))) '('tableType 'externalTable)))))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tablescheme"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateExternalTableWithTablePrefix) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ pragma TablePathPrefix='/aba';
+ CREATE EXTERNAL TABLE mytable (
+ a int
+ ) WITH (
+ DATA_SOURCE="mydatasource",
+ LOCATION="/folder1/*"
+ );
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, "/aba/mydatasource");
+ UNIT_ASSERT_STRING_CONTAINS(line, "/aba/mytable");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("tablescheme"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateExternalTableObjectStorage) {
+ auto res = SqlToYql(R"sql(
+ USE plato;
+ CREATE EXTERNAL TABLE mytable (
+ a int,
+ year Int
+ ) WITH (
+ DATA_SOURCE="/Root/mydatasource",
+ LOCATION="/folder1/*",
+ FORMAT="json_as_string",
+ `projection.enabled`="true",
+ `projection.year.type`="integer",
+ `projection.year.min`="2010",
+ `projection.year.max`="2022",
+ `projection.year.interval`="1",
+ `projection.month.type`="integer",
+ `projection.month.min`="1",
+ `projection.month.max`="12",
+ `projection.month.interval`="1",
+ `projection.month.digits`="2",
+ `storage.location.template`="${year}/${month}",
+ PARTITONED_BY = "[year, month]"
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString());
+ }
+
+ Y_UNIT_TEST(CreateExternalTableIfNotExists) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE EXTERNAL TABLE IF NOT EXISTS mytable (
+ a int
+ ) WITH (
+ DATA_SOURCE="/Root/mydatasource",
+ LOCATION="/folder1/*"
+ );
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('data_source_path (String '"/Root/mydatasource")) '('location (String '"/folder1/*")))) '('tableType 'externalTable)))))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, "create_if_not_exists");
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateExternalTableOrReplace) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+ CREATE OR REPLACE EXTERNAL TABLE mytable (
+ a int
+ ) WITH (
+ DATA_SOURCE="/Root/mydatasource",
+ LOCATION="/folder1/*"
+ );
+ )");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('data_source_path (String '"/Root/mydatasource")) '('location (String '"/folder1/*")))) '('tableType 'externalTable)))))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, "create_or_replace");
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(AlterExternalTableAddColumn) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ ALTER EXTERNAL TABLE mytable
+ ADD COLUMN my_column int32,
+ RESET (LOCATION);
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('actions '('('addColumns '('('"my_column" (AsOptionalType (DataType 'Int32))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('setTableSettings '('('location)))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('tableType 'externalTable))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alter))#");
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(AlterExternalTableDropColumn) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ ALTER EXTERNAL TABLE mytable
+ DROP COLUMN my_column,
+ SET (Location = "abc", Other_Prop = "42"),
+ SET x 'y';
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('actions '('('dropColumns '('"my_column")#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('setTableSettings '('('location (String '"abc")) '('Other_Prop (String '"42")) '('x (String '"y")))))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('tableType 'externalTable))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alter))#");
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateExternalTableWithBadArguments) {
+#if ANTLR_VER == 3
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL TABLE mytable;
+ )sql" , "<main>:3:41: Error: Unexpected token ';' : syntax error...\n\n");
+#else
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL TABLE mytable;
+ )sql" , "<main>:3:45: Error: mismatched input ';' expecting '('\n");
+#endif
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL TABLE mytable (
+ a int
+ );
+ )sql" , "<main>:4:23: Error: DATA_SOURCE requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL TABLE mytable (
+ a int
+ ) WITH (
+ DATA_SOURCE="/Root/mydatasource"
+ );
+ )sql" , "<main>:6:33: Error: LOCATION requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL TABLE mytable (
+ a int
+ ) WITH (
+ LOCATION="/folder1/*"
+ );
+ )sql" , "<main>:6:30: Error: DATA_SOURCE requires key\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE EXTERNAL TABLE mytable (
+ a int,
+ PRIMARY KEY(a)
+ ) WITH (
+ DATA_SOURCE="/Root/mydatasource",
+ LOCATION="/folder1/*"
+ );
+ )sql" , "<main>:8:30: Error: PRIMARY KEY is not supported for external table\n");
+ }
+
+ Y_UNIT_TEST(DropExternalTable) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ DROP EXTERNAL TABLE MyExternalTable;
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("tablescheme"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DropExternalTableWithTablePrefix) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ pragma TablePathPrefix='/aba';
+ DROP EXTERNAL TABLE MyExternalTable;
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, "/aba/MyExternalTable");
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'tablescheme"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DropExternalTableIfExists) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ DROP EXTERNAL TABLE IF EXISTS MyExternalTable;
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("tablescheme"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop_if_exists"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+}
+
+Y_UNIT_TEST_SUITE(TopicsDDL) {
+ void TestQuery(const TString& query, bool expectOk = true) {
+ TStringBuilder finalQuery;
+
+ finalQuery << "use plato;" << Endl << query;
+ auto res = SqlToYql(finalQuery, 10, "kikimr");
+ if (expectOk) {
+ UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString());
+ } else {
+ UNIT_ASSERT(!res.IsOk());
+ }
+ }
+
+ Y_UNIT_TEST(CreateTopicSimple) {
+ TestQuery(R"(
+ CREATE TOPIC topic1;
+ )");
+ TestQuery(R"(
+ CREATE TOPIC `cluster1.topic1`;
+ )");
+ TestQuery(R"(
+ CREATE TOPIC topic1 WITH (metering_mode = "str_value", partition_count_limit = 123, retention_period = Interval('PT1H'));
+ )");
+ }
+
+ Y_UNIT_TEST(CreateTopicConsumer) {
+ TestQuery(R"(
+ CREATE TOPIC topic1 (CONSUMER cons1);
+ )");
+ TestQuery(R"(
+ CREATE TOPIC topic1 (CONSUMER cons1, CONSUMER cons2 WITH (important = false));
+ )");
+ TestQuery(R"(
+ CREATE TOPIC topic1 (CONSUMER cons1, CONSUMER cons2 WITH (important = false)) WITH (supported_codecs = "1,2,3");
+ )");
+ }
+
+ Y_UNIT_TEST(AlterTopicSimple) {
+ TestQuery(R"(
+ ALTER TOPIC topic1 SET (retention_period = Interval('PT1H'));
+ )");
+ TestQuery(R"(
+ ALTER TOPIC topic1 SET (retention_storage_mb = 3, partition_count_limit = 50);
+ )");
+ TestQuery(R"(
+ ALTER TOPIC topic1 RESET (supported_codecs, retention_period);
+ )");
+ TestQuery(R"(
+ ALTER TOPIC topic1 RESET (partition_write_speed_bytes_per_second),
+ SET (partition_write_burst_bytes = 11111, min_active_partitions = 1);
+ )");
+ }
+ Y_UNIT_TEST(AlterTopicConsumer) {
+ TestQuery(R"(
+ ALTER TOPIC topic1 ADD CONSUMER consumer1,
+ ADD CONSUMER consumer2 WITH (important = false, supported_codecs = "RAW"),
+ ALTER CONSUMER consumer3 SET (important = false, read_from = 1),
+ ALTER CONSUMER consumer3 RESET (supported_codecs),
+ DROP CONSUMER consumer4,
+ SET (partition_count_limit = 11, retention_period = Interval('PT1H')),
+ RESET(metering_mode)
+ )");
+ }
+ Y_UNIT_TEST(DropTopic) {
+ TestQuery(R"(
+ DROP TOPIC topic1;
+ )");
+ }
+
+ Y_UNIT_TEST(TopicBadRequests) {
+ TestQuery(R"(
+ CREATE TOPIC topic1();
+ )", false);
+ TestQuery(R"(
+ CREATE TOPIC topic1 SET setting1 = value1;
+ )", false);
+ TestQuery(R"(
+ ALTER TOPIC topic1 SET setting1 value1;
+ )", false);
+ TestQuery(R"(
+ ALTER TOPIC topic1 RESET setting1;
+ )", false);
+
+ TestQuery(R"(
+ ALTER TOPIC topic1 DROP CONSUMER consumer4 WITH (k1 = v1);
+ )", false);
+
+ TestQuery(R"(
+ CREATE TOPIC topic1 WITH (retention_period = 123);
+ )", false);
+ TestQuery(R"(
+ CREATE TOPIC topic1 (CONSUMER cons1, CONSUMER cons1 WITH (important = false));
+ )", false);
+ TestQuery(R"(
+ CREATE TOPIC topic1 (CONSUMER cons1 WITH (bad_option = false));
+ )", false);
+ TestQuery(R"(
+ ALTER TOPIC topic1 ADD CONSUMER cons1, ALTER CONSUMER cons1 RESET (important);
+ )", false);
+ TestQuery(R"(
+ ALTER TOPIC topic1 ADD CONSUMER consumer1,
+ ALTER CONSUMER consumer3 SET (supported_codecs = "RAW", read_from = 1),
+ ALTER CONSUMER consumer3 RESET (supported_codecs);
+ )", false);
+ TestQuery(R"(
+ ALTER TOPIC topic1 ADD CONSUMER consumer1,
+ ALTER CONSUMER consumer3 SET (supported_codecs = "RAW", read_from = 1),
+ ALTER CONSUMER consumer3 SET (read_from = 2);
+ )", false);
+ }
+
+ Y_UNIT_TEST(TopicWithPrefix) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+ PRAGMA TablePathPrefix = '/database/path/to/tables';
+ ALTER TOPIC `my_table/my_feed` ADD CONSUMER `my_consumer`;
+ )");
+ UNIT_ASSERT(res.Root);
+
+ TWordCountHive elementStat = {{TString("/database/path/to/tables/my_table/my_feed"), 0}, {"topic", 0}};
+ VerifyProgram(res, elementStat);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["topic"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["/database/path/to/tables/my_table/my_feed"]);
+ }
+}
+
+Y_UNIT_TEST_SUITE(BlockEnginePragma) {
+ Y_UNIT_TEST(Basic) {
+ const TVector<TString> values = {"auto", "force", "disable"};
+ for (const auto& value : values) {
+ const auto query = TStringBuilder() << "pragma Blockengine='" << value << "'; select 1;";
+ NYql::TAstParseResult res = SqlToYql(query);
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ Y_UNUSED(word);
+ UNIT_ASSERT_STRING_CONTAINS(line, TStringBuilder() << R"(Configure! world (DataSource '"config") '"BlockEngine" '")" << value << "\"");
+ };
+
+ TWordCountHive elementStat({"BlockEngine"});
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT(elementStat["BlockEngine"] == ((value == "disable") ? 0 : 1));
+ }
+ }
+
+ Y_UNIT_TEST(UnknownSetting) {
+ ExpectFailWithError("use plato; pragma BlockEngine='foo';",
+ "<main>:1:31: Error: Expected `disable|auto|force' argument for: BlockEngine\n");
+ }
+}
+
+Y_UNIT_TEST_SUITE(TViewSyntaxTest) {
+ Y_UNIT_TEST(CreateViewSimple) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+ CREATE VIEW TheView WITH (security_invoker = TRUE) AS SELECT 1;
+ )"
+ );
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+ }
+
+ Y_UNIT_TEST(CreateViewIfNotExists) {
+ constexpr const char* name = "TheView";
+ NYql::TAstParseResult res = SqlToYql(std::format(R"(
+ USE plato;
+ CREATE VIEW IF NOT EXISTS {} AS SELECT 1;
+ )", name
+ ));
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_STRING_CONTAINS(line, name);
+ UNIT_ASSERT_STRING_CONTAINS(line, "createObjectIfNotExists");
+ }
+ };
+
+ TWordCountHive elementStat = { {"Write!"} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
+ }
+
+ Y_UNIT_TEST(CreateViewFromTable) {
+ constexpr const char* path = "/PathPrefix/TheView";
+ constexpr const char* query = R"(
+ SELECT * FROM SomeTable
+ )";
+
+ NYql::TAstParseResult res = SqlToYql(std::format(R"(
+ USE plato;
+ CREATE VIEW `{}` WITH (security_invoker = TRUE) AS {};
+ )",
+ path,
+ query
+ )
+ );
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_STRING_CONTAINS(line, path);
+ UNIT_ASSERT_STRING_CONTAINS(line, "createObject");
+ }
+ };
+ TWordCountHive elementStat = { {"Write!"} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
+ }
+
+ Y_UNIT_TEST(CheckReconstructedQuery) {
+ constexpr const char* path = "/PathPrefix/TheView";
+ constexpr const char* query = R"(
+ SELECT * FROM FirstTable JOIN SecondTable ON FirstTable.key == SecondTable.key
+ )";
+
+ NYql::TAstParseResult res = SqlToYql(std::format(R"(
+ USE plato;
+ CREATE VIEW `{}` WITH (security_invoker = TRUE) AS {};
+ )",
+ path,
+ query
+ )
+ );
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TString reconstructedQuery = ToString(Tokenize(query));
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ if (word == "query_text") {
+ UNIT_ASSERT_STRING_CONTAINS(line, reconstructedQuery);
+ }
+ };
+ TWordCountHive elementStat = { {"Write!"} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
+ }
+
+ Y_UNIT_TEST(DropView) {
+ constexpr const char* path = "/PathPrefix/TheView";
+ NYql::TAstParseResult res = SqlToYql(std::format(R"(
+ USE plato;
+ DROP VIEW `{}`;
+ )",
+ path
+ )
+ );
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_STRING_CONTAINS(line, path);
+ UNIT_ASSERT_STRING_CONTAINS(line, "dropObject");
+ }
+ };
+ TWordCountHive elementStat = { {"Write!"} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
+ }
+
+ Y_UNIT_TEST(DropViewIfExists) {
+ constexpr const char* name = "TheView";
+ NYql::TAstParseResult res = SqlToYql(std::format(R"(
+ USE plato;
+ DROP VIEW IF EXISTS {};
+ )", name
+ ));
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_STRING_CONTAINS(line, name);
+ UNIT_ASSERT_STRING_CONTAINS(line, "dropObjectIfExists");
+ }
+ };
+
+ TWordCountHive elementStat = { {"Write!"} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
+ }
+
+ Y_UNIT_TEST(CreateViewWithTablePrefix) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+ PRAGMA TablePathPrefix='/PathPrefix';
+ CREATE VIEW TheView WITH (security_invoker = TRUE) AS SELECT 1;
+ )"
+ );
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write!") {
+ UNIT_ASSERT_STRING_CONTAINS(line, "/PathPrefix/TheView");
+ UNIT_ASSERT_STRING_CONTAINS(line, "createObject");
+ }
+ };
+
+ TWordCountHive elementStat = { {"Write!"} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
+ }
+
+ Y_UNIT_TEST(DropViewWithTablePrefix) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ USE plato;
+ PRAGMA TablePathPrefix='/PathPrefix';
+ DROP VIEW TheView;
+ )"
+ );
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, "/PathPrefix/TheView");
+ UNIT_ASSERT_STRING_CONTAINS(line, "dropObject");
+ }
+ };
+
+ TWordCountHive elementStat = { {"Write!"} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1);
+ }
+
+ Y_UNIT_TEST(YtAlternativeSchemaSyntax) {
+ NYql::TAstParseResult res = SqlToYql(R"(
+ SELECT * FROM plato.Input WITH schema(y Int32, x String not null);
+ )");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "userschema") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos,
+ line.find(R"__('('('"userschema" (StructType '('"y" (AsOptionalType (DataType 'Int32))) '('"x" (DataType 'String))))))__"));
+ }
+ };
+
+ TWordCountHive elementStat = {{TString("userschema"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["userschema"]);
+ }
+
+ Y_UNIT_TEST(UseViewAndFullColumnId) {
+ NYql::TAstParseResult res = SqlToYql("USE plato; SELECT Input.x FROM Input VIEW uitzicht;");
+ UNIT_ASSERT(res.Root);
+
+ TWordCountHive elementStat = {{TString("SqlAccess"), 0}, {"SqlProjectItem", 0}, {"Read!", 0}};
+ VerifyProgram(res, elementStat);
+ UNIT_ASSERT_VALUES_EQUAL(0, elementStat["SqlAccess"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SqlProjectItem"]);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Read!"]);
+ }
+}
+
+Y_UNIT_TEST_SUITE(CompactNamedExprs) {
+ Y_UNIT_TEST(SourceCallablesInWrongContext) {
+ TString query = R"(
+ pragma CompactNamedExprs;
+ $foo = %s();
+ select $foo from plato.Input;
+ )";
+
+ THashMap<TString, TString> errs = {
+ {"TableRow", "<main>:3:20: Error: TableRow requires data source\n"},
+ {"JoinTableRow", "<main>:3:20: Error: JoinTableRow requires data source\n"},
+ {"TableRecordIndex", "<main>:3:20: Error: Unable to use function: TableRecord without source\n"},
+ {"TablePath", "<main>:3:20: Error: Unable to use function: TablePath without source\n"},
+ {"SystemMetadata", "<main>:3:20: Error: Unable to use function: SystemMetadata without source\n"},
+ };
+
+ for (TString callable : { "TableRow", "JoinTableRow", "TableRecordIndex", "TablePath", "SystemMetadata"}) {
+ auto req = Sprintf(query.c_str(), callable.c_str());
+ ExpectFailWithError(req, errs[callable]);
+ }
+ }
+
+ Y_UNIT_TEST(ValidateUnusedExprs) {
+ TString query = R"(
+ pragma warning("disable", "4527");
+ pragma CompactNamedExprs;
+ pragma ValidateUnusedExprs;
+
+ $foo = count(1);
+ select 1;
+ )";
+ ExpectFailWithError(query, "<main>:6:20: Error: Aggregation is not allowed in this context\n");
+ query = R"(
+ pragma warning("disable", "4527");
+ pragma CompactNamedExprs;
+ pragma ValidateUnusedExprs;
+
+ define subquery $x() as
+ select count(1, 2);
+ end define;
+ select 1;
+ )";
+ ExpectFailWithError(query, "<main>:7:24: Error: Aggregation function Count requires exactly 1 argument(s), given: 2\n");
+ }
+
+ Y_UNIT_TEST(DisableValidateUnusedExprs) {
+ TString query = R"(
+ pragma warning("disable", "4527");
+ pragma CompactNamedExprs;
+ pragma DisableValidateUnusedExprs;
+
+ $foo = count(1);
+ select 1;
+ )";
+ SqlToYql(query).IsOk();
+ query = R"(
+ pragma warning("disable", "4527");
+ pragma CompactNamedExprs;
+ pragma DisableValidateUnusedExprs;
+
+ define subquery $x() as
+ select count(1, 2);
+ end define;
+ select 1;
+ )";
+ SqlToYql(query).IsOk();
+ }
+}
+
+Y_UNIT_TEST_SUITE(ResourcePool) {
+ Y_UNIT_TEST(CreateResourcePool) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE RESOURCE POOL MyResourcePool WITH (
+ CONCURRENT_QUERY_LIMIT=20,
+ QUERY_CANCEL_AFTER_SECONDS=86400,
+ QUEUE_TYPE="FIFO"
+ );
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"concurrent_query_limit" (Int32 '"20")) '('"query_cancel_after_seconds" (Int32 '"86400")) '('"queue_type" '"FIFO"))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateResourcePoolWithBadArguments) {
+#if ANTLR_VER == 3
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE RESOURCE POOL MyResourcePool;
+ )sql" , "<main>:3:47: Error: Unexpected token ';' : syntax error...\n\n");
+#else
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE RESOURCE POOL MyResourcePool;
+ )sql" , "<main>:3:47: Error: mismatched input ';' expecting WITH\n");
+#endif
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE RESOURCE POOL MyResourcePool WITH (
+ DUPLICATE_SETTING="first_value",
+ DUPLICATE_SETTING="second_value"
+ );
+ )sql" , "<main>:5:21: Error: DUPLICATE_SETTING duplicate keys\n");
+ }
+
+ Y_UNIT_TEST(AlterResourcePool) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ ALTER RESOURCE POOL MyResourcePool
+ SET (CONCURRENT_QUERY_LIMIT = 30, Weight = 5, QUEUE_TYPE = "UNORDERED"),
+ RESET (Query_Cancel_After_Seconds, Query_Count_Limit);
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alterObject))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('features '('('"concurrent_query_limit" (Int32 '"30")) '('"queue_type" '"UNORDERED") '('"weight" (Int32 '"5")))))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('resetFeatures '('"query_cancel_after_seconds" '"query_count_limit")))#");
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DropResourcePool) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ DROP RESOURCE POOL MyResourcePool;
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+}
+
+Y_UNIT_TEST_SUITE(BackupCollection) {
+ Y_UNIT_TEST(CreateBackupCollection) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE BACKUP COLLECTION TestCollection WITH (
+ STORAGE="local",
+ TAG="test" -- for testing purposes, not a real thing
+ );
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"storage" (String '"local")))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"tag" (String '"test"))))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('entries '()))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'create"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateBackupCollectionWithDatabase) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE BACKUP COLLECTION TestCollection DATABASE WITH (
+ STORAGE="local",
+ TAG="test" -- for testing purposes, not a real thing
+ );
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"storage" (String '"local")))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"tag" (String '"test"))))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('entries '('('('type 'database)))))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'create"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateBackupCollectionWithTables) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE BACKUP COLLECTION TestCollection (
+ TABLE someTable,
+ TABLE `prefix/anotherTable`
+ ) WITH (
+ STORAGE="local",
+ TAG="test" -- for testing purposes, not a real thing
+ );
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"storage" (String '"local")))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"tag" (String '"test"))))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('entries '('('('type 'table) '('path '"someTable")) '('('type 'table) '('path '"prefix/anotherTable")))))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'create"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateBackupCollectionWithBadArguments) {
+#if ANTLR_VER == 3
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE BACKUP COLLECTION TestCollection;
+ )sql" , "<main>:3:51: Error: Unexpected token ';' : syntax error...\n\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE BACKUP COLLECTION TABLE TestCollection;
+ )sql" , "<main>:3:43: Error: Unexpected token 'TestCollection' : syntax error...\n\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE BACKUP COLLECTION DATABASE `test` TestCollection;
+ )sql" , "<main>:3:46: Error: Unexpected token '`test`' : syntax error...\n\n");
+#else
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE BACKUP COLLECTION TestCollection;
+ )sql" , "<main>:3:55: Error: mismatched input ';' expecting {'(', DATABASE, WITH}\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE BACKUP COLLECTION TABLE TestCollection;
+ )sql" , "<main>:3:47: Error: mismatched input 'TestCollection' expecting {'(', DATABASE, WITH}\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE BACKUP COLLECTION DATABASE `test` TestCollection;
+ )sql" , "<main>:3:50: Error: mismatched input '`test`' expecting {'(', DATABASE, WITH}\n");
+#endif
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE BACKUP COLLECTION TestCollection WITH (
+ DUPLICATE_SETTING="first_value",
+ DUPLICATE_SETTING="second_value"
+ );
+ )sql" , "<main>:5:21: Error: DUPLICATE_SETTING duplicate keys\n");
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE BACKUP COLLECTION TestCollection WITH (
+ INT_SETTING=1
+ );
+ )sql" , "<main>:4:21: Error: INT_SETTING value should be a string literal\n");
+ }
+
+ Y_UNIT_TEST(AlterBackupCollection) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ ALTER BACKUP COLLECTION TestCollection
+ SET (STORAGE="remote"), -- also just for test
+ SET (TAG1 = "123"),
+ RESET (TAG2, TAG3);
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alter))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"storage" (String '"remote")))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('"tag1" (String '"123"))))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('resetSettings '('"tag2" '"tag3")))#");
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(AlterBackupCollectionEntries) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ ALTER BACKUP COLLECTION TestCollection
+ DROP TABLE `test`,
+ ADD DATABASE;
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alter))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('alterEntries)#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('type 'table) '('path '"test") '('action 'drop)))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('type 'database) '('action 'add)))#");
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DropBackupCollection) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ DROP BACKUP COLLECTION TestCollection;
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+}
+
+Y_UNIT_TEST_SUITE(ResourcePoolClassifier) {
+ Y_UNIT_TEST(CreateResourcePoolClassifier) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE RESOURCE POOL CLASSIFIER MyResourcePoolClassifier WITH (
+ RANK=20,
+ RESOURCE_POOL='wgUserQueries',
+ MEMBER_NAME='yandex_query@abc'
+ );
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"member_name" '"yandex_query@abc") '('"rank" (Int32 '"20")) '('"resource_pool" '"wgUserQueries"))#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(CreateResourcePoolClassifierWithBadArguments) {
+#if ANTLR_VER == 3
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE RESOURCE POOL CLASSIFIER MyResourcePoolClassifier;
+ )sql" , "<main>:3:68: Error: Unexpected token ';' : syntax error...\n\n");
+#else
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE RESOURCE POOL CLASSIFIER MyResourcePoolClassifier;
+ )sql" , "<main>:3:72: Error: mismatched input ';' expecting WITH\n");
+#endif
+
+ ExpectFailWithError(R"sql(
+ USE plato;
+ CREATE RESOURCE POOL CLASSIFIER MyResourcePoolClassifier WITH (
+ DUPLICATE_SETTING="first_value",
+ DUPLICATE_SETTING="second_value"
+ );
+ )sql" , "<main>:5:21: Error: DUPLICATE_SETTING duplicate keys\n");
+ }
+
+ Y_UNIT_TEST(AlterResourcePoolClassifier) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ ALTER RESOURCE POOL CLASSIFIER MyResourcePoolClassifier
+ SET (RANK = 30, Weight = 5, MEMBER_NAME = "test@user"),
+ RESET (Resource_Pool);
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#(('mode 'alterObject))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('features '('('"member_name" '"test@user") '('"rank" (Int32 '"30")) '('"weight" (Int32 '"5")))))#");
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('('resetFeatures '('"resource_pool")))#");
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(DropResourcePoolClassifier) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ DROP RESOURCE POOL CLASSIFIER MyResourcePoolClassifier;
+ )sql");
+ UNIT_ASSERT(res.Root);
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'features"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObject"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0}};
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(BacktickMatching) {
+ auto req = "select\n"
+ " 1 as `Schema has \\`RealCost\\``\n"
+ " -- foo`bar";
+ auto res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+ res = SqlToYqlWithAnsiLexer(req);
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+
+ req = "select 1 as `a``b`, 2 as ````, 3 as `\\x60a\\x60`, 4 as ```b```, 5 as `\\`c\\``";
+ res = SqlToYql(req);
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+ res = SqlToYqlWithAnsiLexer(req);
+ UNIT_ASSERT(res.Root);
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+ }
+}
+
+Y_UNIT_TEST_SUITE(Backup) {
+ Y_UNIT_TEST(Simple) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ BACKUP TestCollection;
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
+ UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("'Incremental"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'backup"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(Incremental) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ BACKUP TestCollection INCREMENTAL;
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'backupIncremental"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+}
+
+Y_UNIT_TEST_SUITE(Restore) {
+ Y_UNIT_TEST(Simple) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ RESTORE TestCollection;
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'restore"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+
+ Y_UNIT_TEST(AtPoint) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ RESTORE TestCollection AT '2024-06-16_20-14-02';
+ )sql");
+ UNIT_ASSERT_C(res.Root, res.Issues.ToString());
+
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find(R"#('"TestCollection")#"));
+ UNIT_ASSERT_STRING_CONTAINS(line, R"#('at '"2024-06-16_20-14-02")#");
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'restore"));
+ }
+ };
+
+ TWordCountHive elementStat = { {TString("Write"), 0} };
+ VerifyProgram(res, elementStat, verifyLine);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ }
+}
+
+Y_UNIT_TEST_SUITE(ColumnFamily) {
+ Y_UNIT_TEST(CompressionLevelCorrectUsage) {
+ NYql::TAstParseResult res = SqlToYql(R"( use plato;
+ CREATE TABLE tableName (
+ Key Uint32 FAMILY default,
+ Value String FAMILY family1,
+ PRIMARY KEY (Key),
+ FAMILY default (
+ DATA = "test",
+ COMPRESSION = "lz4",
+ COMPRESSION_LEVEL = 5
+ ),
+ FAMILY family1 (
+ DATA = "test",
+ COMPRESSION = "lz4",
+ COMPRESSION_LEVEL = 3
+ )
+ );
+ )");
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+ TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) {
+ if (word == "Write") {
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("compression_level"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("5"));
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("3"));
+ }
+ };
+
+ TWordCountHive elementStat = { { TString("Write"), 0 }, { TString("compression_level"), 0 } };
+ VerifyProgram(res, elementStat, verifyLine);
+ UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]);
+ UNIT_ASSERT_VALUES_EQUAL(2, elementStat["compression_level"]);
+ }
+
+ Y_UNIT_TEST(FieldDataIsNotString) {
+ NYql::TAstParseResult res = SqlToYql(R"( use plato;
+ CREATE TABLE tableName (
+ Key Uint32 FAMILY default,
+ PRIMARY KEY (Key),
+ FAMILY default (
+ DATA = 1,
+ COMPRESSION = "lz4",
+ COMPRESSION_LEVEL = 5
+ )
+ );
+ )");
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 1);
+ UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "DATA value should be a string literal");
+ }
+
+ Y_UNIT_TEST(FieldCompressionIsNotString) {
+ NYql::TAstParseResult res = SqlToYql(R"( use plato;
+ CREATE TABLE tableName (
+ Key Uint32 FAMILY default,
+ PRIMARY KEY (Key),
+ FAMILY default (
+ DATA = "test",
+ COMPRESSION = 2,
+ COMPRESSION_LEVEL = 5
+ ),
+ );
+ )");
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 1);
+ UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "COMPRESSION value should be a string literal");
+ }
+
+ Y_UNIT_TEST(FieldCompressionLevelIsNotInteger) {
+ NYql::TAstParseResult res = SqlToYql(R"( use plato;
+ CREATE TABLE tableName (
+ Key Uint32 FAMILY default,
+ PRIMARY KEY (Key),
+ FAMILY default (
+ DATA = "test",
+ COMPRESSION = "lz4",
+ COMPRESSION_LEVEL = "5"
+ )
+ );
+ )");
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 1);
+ UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "COMPRESSION_LEVEL value should be an integer");
+ }
+
+ Y_UNIT_TEST(AlterCompressionCorrectUsage) {
+ NYql::TAstParseResult res = SqlToYql(R"( use plato;
+ ALTER TABLE tableName ALTER FAMILY default SET COMPRESSION "lz4";
+ )");
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+ }
+
+ Y_UNIT_TEST(AlterCompressionFieldIsNotString) {
+ NYql::TAstParseResult res = SqlToYql(R"( use plato;
+ ALTER TABLE tableName ALTER FAMILY default SET COMPRESSION lz4;
+ )");
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 1);
+#if ANTLR_VER == 3
+ UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "Unexpected token 'lz4' : cannot match to any predicted input");
+#else
+ UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "mismatched input 'lz4' expecting {STRING_VALUE, DIGITS, INTEGER_VALUE}");
+#endif
+ }
+
+ Y_UNIT_TEST(AlterCompressionLevelCorrectUsage) {
+ NYql::TAstParseResult res = SqlToYql(R"( use plato;
+ ALTER TABLE tableName ALTER FAMILY default SET COMPRESSION_LEVEL 5;
+ )");
+ UNIT_ASSERT(res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 0);
+ }
+
+ Y_UNIT_TEST(AlterCompressionLevelFieldIsNotInteger) {
+ NYql::TAstParseResult res = SqlToYql(R"( use plato;
+ ALTER TABLE tableName ALTER FAMILY default SET COMPRESSION_LEVEL "5";
+ )");
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 1);
+ UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "COMPRESSION_LEVEL value should be an integer");
+ }
+}
+
+Y_UNIT_TEST_SUITE(Transfer) {
+ Y_UNIT_TEST(Lambda) {
+ NYql::TAstParseResult res = SqlToYql(R"( use plato;
+ -- Русский коммент, empty statement
+ ;
+
+ -- befor comment
+ $a = "А";
+
+ SELECT * FROM Input;
+
+ $b = ($x) -> { return $a || $x; };
+
+ CREATE TRANSFER `TransferName`
+ FROM `TopicName` TO `TableName`
+ USING ($x) -> {
+ -- internal comment
+ return $b($x);
+ }
+ WITH (
+ CONNECTION_STRING = "grpc://localhost:2135/?database=/Root"
+ );
+ )");
+
+ UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString());
+ UNIT_ASSERT_VALUES_EQUAL_C(res.Issues.Size(), 0, res.Issues.ToString());
+
+ const auto programm = GetPrettyPrint(res);
+
+ Cerr << ">>>>> Root " << programm << Endl;
+ auto expected = R"('transformLambda 'use plato;
+-- befor comment
+ $a = "А";
+$b = ($x) -> { return $a || $x; };
+$__ydb_transfer_lambda = ($x) -> {
+ -- internal comment
+ return $b($x);
+ };
+))";
+
+ UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, programm.find(expected));
+
+ }
+}
+
+Y_UNIT_TEST_SUITE(MatchRecognizeMeasuresAggregation) {
+ Y_UNIT_TEST(InsideSelect) {
+ ExpectFailWithError(R"sql(
+ SELECT FIRST(0), LAST(1);
+ )sql",
+ "<main>:2:20: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
+ "<main>:2:30: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
+ );
+ }
+
+ Y_UNIT_TEST(OutsideSelect) {
+ ExpectFailWithError(R"sql(
+ $lambda = ($x) -> (FIRST($x) + LAST($x));
+ SELECT $lambda(x) FROM plato.Input;
+ )sql",
+ "<main>:2:32: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
+ "<main>:2:44: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
+ );
+ }
+
+ Y_UNIT_TEST(AsAggregateFunction) {
+ ExpectFailWithError(R"sql(
+ SELECT FIRST(x), LAST(x) FROM plato.Input;
+ )sql",
+ "<main>:2:20: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
+ "<main>:2:30: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
+ );
+ }
+
+ Y_UNIT_TEST(AsWindowFunction) {
+ ExpectFailWithError(R"sql(
+ SELECT FIRST(x) OVER(), LAST(x) OVER() FROM plato.Input;
+ )sql",
+ "<main>:2:20: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
+ "<main>:2:37: Error: Cannot use FIRST and LAST outside the MATCH_RECOGNIZE context\n"
+ );
+ }
+}
+
+Y_UNIT_TEST_SUITE(OlapPartitionCount) {
+ Y_UNIT_TEST(CorrectUsage) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE TABLE `mytable` (id Uint32, PRIMARY KEY (id))
+ PARTITION BY HASH(id)
+ WITH (STORE = COLUMN, PARTITION_COUNT = 8);
+ )sql");
+
+ UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString());
+ }
+
+ Y_UNIT_TEST(UseWithoutColumnStore) {
+ NYql::TAstParseResult res = SqlToYql(R"sql(
+ USE plato;
+ CREATE TABLE `mytable` (id Uint32, PRIMARY KEY (id))
+ WITH (PARTITION_COUNT = 8);
+ )sql");
+
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT(res.Issues.Size() == 1);
+ UNIT_ASSERT_STRING_CONTAINS(res.Issues.ToString(), "PARTITION_COUNT can be used only with STORE=COLUMN");
+ }
+}
diff --git a/yql/essentials/sql/v1/ya.make b/yql/essentials/sql/v1/ya.make
index 08fd499b3f..1d2105f6fa 100644
--- a/yql/essentials/sql/v1/ya.make
+++ b/yql/essentials/sql/v1/ya.make
@@ -56,6 +56,7 @@ GENERATE_ENUM_SERIALIZATION(sql_call_param.h)
END()
RECURSE(
+ complete
format
lexer
perf
diff --git a/yql/essentials/tests/common/test_framework/yql_utils.py b/yql/essentials/tests/common/test_framework/yql_utils.py
index 8314349358..2136729be9 100644
--- a/yql/essentials/tests/common/test_framework/yql_utils.py
+++ b/yql/essentials/tests/common/test_framework/yql_utils.py
@@ -52,7 +52,7 @@ def do_custom_query_check(res, sql_query):
def do_custom_error_check(res, sql_query):
err_string = None
- custom_error = re.search(r"/\* custom error:(.*)\*/", sql_query)
+ custom_error = re.search(r"/\* custom error:(.*?)\*/", sql_query, re.DOTALL)
if custom_error:
err_string = custom_error.group(1).strip()
assert err_string, 'Expected custom error check in test.\nTest error: %s' % res.std_err
diff --git a/yql/essentials/tools/ya.make b/yql/essentials/tools/ya.make
index becd8907f6..248945e3fb 100644
--- a/yql/essentials/tools/ya.make
+++ b/yql/essentials/tools/ya.make
@@ -11,6 +11,7 @@ RECURSE(
udf_dep_stub
udf_probe
udf_resolver
+ yql_complete
yql_facade_run
yql_linter
)
diff --git a/yql/essentials/tools/yql_complete/ya.make b/yql/essentials/tools/yql_complete/ya.make
new file mode 100644
index 0000000000..627abb2eeb
--- /dev/null
+++ b/yql/essentials/tools/yql_complete/ya.make
@@ -0,0 +1,12 @@
+PROGRAM()
+
+PEERDIR(
+ library/cpp/getopt
+ yql/essentials/sql/v1/complete
+)
+
+SRCS(
+ yql_complete.cpp
+)
+
+END()
diff --git a/yql/essentials/tools/yql_complete/yql_complete.cpp b/yql/essentials/tools/yql_complete/yql_complete.cpp
new file mode 100644
index 0000000000..289573190b
--- /dev/null
+++ b/yql/essentials/tools/yql_complete/yql_complete.cpp
@@ -0,0 +1,53 @@
+#include <yql/essentials/sql/v1/complete/sql_complete.h>
+
+#include <library/cpp/getopt/last_getopt.h>
+#include <util/stream/file.h>
+
+int Run(int argc, char* argv[]) {
+ NLastGetopt::TOpts opts = NLastGetopt::TOpts::Default();
+
+ TString inFileName;
+ TMaybe<ui64> pos;
+ opts.AddLongOption('i', "input", "input file").RequiredArgument("input").StoreResult(&inFileName);
+ opts.AddLongOption('p', "pos", "position").StoreResult(&pos);
+ opts.SetFreeArgsNum(0);
+ opts.AddHelpOption();
+
+ NLastGetopt::TOptsParseResult res(&opts, argc, argv);
+
+ THolder<TUnbufferedFileInput> inFile;
+ if (!inFileName.empty()) {
+ inFile.Reset(new TUnbufferedFileInput(inFileName));
+ }
+ IInputStream& in = inFile ? *inFile.Get() : Cin;
+
+ auto queryString = in.ReadAll();
+ auto engine = NSQLComplete::MakeSqlCompletionEngine();
+ NSQLComplete::TCompletionInput input;
+ input.Text = queryString;
+ if (pos) {
+ input.CursorPosition = *pos;
+ } else {
+ input.CursorPosition = queryString.size();
+ }
+
+ auto output = engine->Complete(input);
+ for (const auto& c : output.Candidates) {
+ Cout << "[" << c.Kind << "] " << c.Content << "\n";
+ }
+
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ try {
+ return Run(argc, argv);
+ } catch (const yexception& e) {
+ Cerr << "Caught exception:" << e.what() << Endl;
+ return 1;
+ } catch (...) {
+ Cerr << CurrentExceptionMessage() << Endl;
+ return 1;
+ }
+ return 0;
+}
diff --git a/yql/essentials/udfs/common/datetime2/datetime_udf.cpp b/yql/essentials/udfs/common/datetime2/datetime_udf.cpp
index c0157f2365..c56b22a7fa 100644
--- a/yql/essentials/udfs/common/datetime2/datetime_udf.cpp
+++ b/yql/essentials/udfs/common/datetime2/datetime_udf.cpp
@@ -13,6 +13,9 @@ using namespace NUdf;
using namespace NYql::DateTime;
extern const char SplitUDF[] = "Split";
+extern const char ToDaysUDF[] = "ToDays";
+extern const char ToHoursUDF[] = "ToHours";
+extern const char ToMinutesUDF[] = "ToMinutes";
extern const char ToSecondsUDF[] = "ToSeconds";
extern const char ToMillisecondsUDF[] = "ToMilliseconds";
extern const char ToMicrosecondsUDF[] = "ToMicroseconds";
@@ -59,7 +62,7 @@ const auto UsecondsInMinute = 60000000ll;
const auto UsecondsInSecond = 1000000ll;
const auto UsecondsInMilliseconds = 1000ll;
-template <const char* TFuncName, typename TResult, ui32 ScaleAfterSeconds>
+template <const char* TFuncName, typename TResult, typename TWResult, ui32 ScaleAfterSeconds>
class TToUnits {
public:
typedef bool TTypeAwareMarker;
@@ -69,6 +72,10 @@ public:
return value * ui32(86400) * TResult(ScaleAfterSeconds);
}
+ static TWResult Date32Core(i32 value) {
+ return value * i64(86400) * TWResult(ScaleAfterSeconds);
+ }
+
template<typename TTzDate>
static TResult TzBlockCore(TBlockItem tzDate);
@@ -91,14 +98,26 @@ public:
return value * TResult(ScaleAfterSeconds);
}
+ static TWResult Datetime64Core(i64 value) {
+ return value * TWResult(ScaleAfterSeconds);
+ }
+
static TResult TimestampCore(ui64 value) {
return TResult(value / (1000000u / ScaleAfterSeconds));
}
+ static TWResult Timestamp64Core(i64 value) {
+ return TWResult(value / (1000000u / ScaleAfterSeconds));
+ }
+
static TSignedResult IntervalCore(i64 value) {
return TSignedResult(value / (1000000u / ScaleAfterSeconds));
}
+ static TWResult Interval64Core(i64 value) {
+ return TWResult(value / (1000000u / ScaleAfterSeconds));
+ }
+
static const TStringRef& Name() {
static auto name = TStringRef(TFuncName, std::strlen(TFuncName));
return name;
@@ -120,125 +139,166 @@ public:
return false;
}
- try {
- auto typeInfoHelper = builder.TypeInfoHelper();
- TTupleTypeInspector tuple(*typeInfoHelper, userType);
- Y_ENSURE(tuple);
- Y_ENSURE(tuple.GetElementsCount() > 0);
- TTupleTypeInspector argsTuple(*typeInfoHelper, tuple.GetElementType(0));
- Y_ENSURE(argsTuple);
- if (argsTuple.GetElementsCount() != 1) {
- builder.SetError("Expected one argument");
- return true;
- }
+ if (!userType) {
+ builder.SetError("User type is missing");
+ return true;
+ }
+ builder.UserType(userType);
- auto argType = argsTuple.GetElementType(0);
- TVector<const TType*> argBlockTypes;
- argBlockTypes.push_back(argType);
+ const auto typeInfoHelper = builder.TypeInfoHelper();
+ TTupleTypeInspector tuple(*typeInfoHelper, userType);
+ Y_ENSURE(tuple, "Tuple with args and options tuples expected");
+ Y_ENSURE(tuple.GetElementsCount() > 0,
+ "Tuple has to contain positional arguments");
- TBlockTypeInspector block(*typeInfoHelper, argType);
- if (block) {
- Y_ENSURE(!block.IsScalar());
- argType = block.GetItemType();
- }
+ TTupleTypeInspector argsTuple(*typeInfoHelper, tuple.GetElementType(0));
+ Y_ENSURE(argsTuple, "Tuple with args expected");
+ if (argsTuple.GetElementsCount() != 1) {
+ builder.SetError("Single argument expected");
+ return true;
+ }
- bool isOptional = false;
- if (auto opt = TOptionalTypeInspector(*typeInfoHelper, argType)) {
- argType = opt.GetItemType();
- isOptional = true;
- }
+ auto argType = argsTuple.GetElementType(0);
+ TVector<const TType*> argBlockTypes;
+ argBlockTypes.push_back(argType);
- TDataTypeInspector data(*typeInfoHelper, argType);
- if (!data) {
- builder.SetError("Expected data type");
- return true;
- }
+ TBlockTypeInspector block(*typeInfoHelper, argType);
+ if (block) {
+ Y_ENSURE(!block.IsScalar());
+ argType = block.GetItemType();
+ }
- auto typeId = data.GetTypeId();
- if (!(typeId == TDataType<TDate>::Id || typeId == TDataType<TTzDate>::Id ||
- typeId == TDataType<TDatetime>::Id || typeId == TDataType<TTzDatetime>::Id ||
- typeId == TDataType<TTimestamp>::Id || typeId == TDataType<TTzTimestamp>::Id ||
- typeId == TDataType<TInterval>::Id)) {
- builder.SetError(TStringBuilder() << "Type " << GetDataTypeInfo(GetDataSlot(typeId)).Name << " is not supported");
- }
+ bool isOptional = false;
+ if (auto opt = TOptionalTypeInspector(*typeInfoHelper, argType)) {
+ argType = opt.GetItemType();
+ isOptional = true;
+ }
- builder.Args()->Add(argsTuple.GetElementType(0)).Done();
- const TType* retType;
- if (typeId != TDataType<TInterval>::Id) {
- retType = builder.SimpleType<TResult>();
- } else {
- retType = builder.SimpleType<TSignedResult>();
- }
- if (isOptional) {
- retType = builder.Optional()->Item(retType).Build();
- }
+ TDataTypeInspector data(*typeInfoHelper, argType);
+ if (!data) {
+ builder.SetError("Data type expected");
+ return true;
+ }
- auto outputType = retType;
- if (block) {
- retType = builder.Block(block.IsScalar())->Item(retType).Build();
- }
+ const auto typeId = data.GetTypeId();
+ const auto features = NUdf::GetDataTypeInfo(NUdf::GetDataSlot(typeId)).Features;
+
+ if (!(features & (NUdf::DateType | NUdf::TzDateType | NUdf::TimeIntervalType))) {
+ builder.SetError(TStringBuilder()
+ << "Type "
+ << GetDataTypeInfo(GetDataSlot(typeId)).Name
+ << " is not supported");
+ return true;
+ }
+
+ builder.Args()->Add(argsTuple.GetElementType(0));
+ const TType* retType;
+ if (features & NUdf::BigDateType) {
+ retType = builder.SimpleType<TWResult>();
+ } else if (features & NUdf::TimeIntervalType) {
+ retType = builder.SimpleType<TSignedResult>();
+ } else {
+ retType = builder.SimpleType<TResult>();
+ }
+
+ if (isOptional) {
+ retType = builder.Optional()->Item(retType).Build();
+ }
- builder.Returns(retType);
+ auto outputType = retType;
+ if (block) {
+ retType = builder.Block(block.IsScalar())->Item(retType).Build();
+ }
+
+ builder.Returns(retType);
+ if (!(features & NUdf::BigDateType)) {
+ // FIXME: Only non-wide overloads support block rewrite now.
builder.SupportsBlocks();
- builder.IsStrict();
+ }
+ builder.IsStrict();
- builder.UserType(userType);
- if (!typesOnly) {
- if (typeId == TDataType<TDate>::Id || typeId == TDataType<TTzDate>::Id) {
- if (block) {
- const auto exec = (typeId == TDataType<TTzDate>::Id)
- ? MakeTzBlockExec<TTzDate, TResult>()
- : UnaryPreallocatedExecImpl<ui16, TResult, DateCore>;
-
- builder.Implementation(new TSimpleArrowUdfImpl(argBlockTypes, outputType, block.IsScalar(),
- exec, builder, TString(name), arrow::compute::NullHandling::INTERSECTION));
- } else {
- builder.Implementation(new TUnaryOverOptionalImpl<ui16, TResult, DateCore>());
- }
+ if (!typesOnly) {
+ if (typeId == TDataType<TDate>::Id || typeId == TDataType<TTzDate>::Id) {
+ if (block) {
+ const auto exec = (typeId == TDataType<TTzDate>::Id)
+ ? MakeTzBlockExec<TTzDate, TResult>()
+ : UnaryPreallocatedExecImpl<ui16, TResult, DateCore>;
+
+ builder.Implementation(new TSimpleArrowUdfImpl(argBlockTypes, outputType, block.IsScalar(),
+ exec, builder, TString(name), arrow::compute::NullHandling::INTERSECTION));
+ } else {
+ builder.Implementation(new TUnaryOverOptionalImpl<ui16, TResult, DateCore>());
}
+ return true;
+ }
- if (typeId == TDataType<TDatetime>::Id || typeId == TDataType<TTzDatetime>::Id) {
- if (block) {
- const auto exec = (typeId == TDataType<TTzDatetime>::Id)
- ? MakeTzBlockExec<TTzDatetime, TResult>()
- : UnaryPreallocatedExecImpl<ui32, TResult, DatetimeCore>;
+ if (typeId == TDataType<TDatetime>::Id || typeId == TDataType<TTzDatetime>::Id) {
+ if (block) {
+ const auto exec = (typeId == TDataType<TTzDatetime>::Id)
+ ? MakeTzBlockExec<TTzDatetime, TResult>()
+ : UnaryPreallocatedExecImpl<ui32, TResult, DatetimeCore>;
- builder.Implementation(new TSimpleArrowUdfImpl(argBlockTypes, outputType, block.IsScalar(),
- exec, builder, TString(name), arrow::compute::NullHandling::INTERSECTION));
- } else {
- builder.Implementation(new TUnaryOverOptionalImpl<ui32, TResult, DatetimeCore>());
- }
+ builder.Implementation(new TSimpleArrowUdfImpl(argBlockTypes, outputType, block.IsScalar(),
+ exec, builder, TString(name), arrow::compute::NullHandling::INTERSECTION));
+ } else {
+ builder.Implementation(new TUnaryOverOptionalImpl<ui32, TResult, DatetimeCore>());
}
+ return true;
+ }
- if (typeId == TDataType<TTimestamp>::Id || typeId == TDataType<TTzTimestamp>::Id) {
- if (block) {
- const auto exec = (typeId == TDataType<TTzTimestamp>::Id)
- ? MakeTzBlockExec<TTzTimestamp, TResult>()
- : UnaryPreallocatedExecImpl<ui64, TResult, TimestampCore>;
+ if (typeId == TDataType<TTimestamp>::Id || typeId == TDataType<TTzTimestamp>::Id) {
+ if (block) {
+ const auto exec = (typeId == TDataType<TTzTimestamp>::Id)
+ ? MakeTzBlockExec<TTzTimestamp, TResult>()
+ : UnaryPreallocatedExecImpl<ui64, TResult, TimestampCore>;
- builder.Implementation(new TSimpleArrowUdfImpl(argBlockTypes, outputType, block.IsScalar(),
- exec, builder, TString(name), arrow::compute::NullHandling::INTERSECTION));
- } else {
- builder.Implementation(new TUnaryOverOptionalImpl<ui64, TResult, TimestampCore>());
- }
+ builder.Implementation(new TSimpleArrowUdfImpl(argBlockTypes, outputType, block.IsScalar(),
+ exec, builder, TString(name), arrow::compute::NullHandling::INTERSECTION));
+ } else {
+ builder.Implementation(new TUnaryOverOptionalImpl<ui64, TResult, TimestampCore>());
}
+ return true;
+ }
- if (typeId == TDataType<TInterval>::Id) {
- if (block) {
- builder.Implementation(new TSimpleArrowUdfImpl(argBlockTypes, outputType, block.IsScalar(),
- UnaryPreallocatedExecImpl<i64, TSignedResult, IntervalCore>, builder, TString(name), arrow::compute::NullHandling::INTERSECTION));
- } else {
- builder.Implementation(new TUnaryOverOptionalImpl<i64, TSignedResult, IntervalCore>());
- }
+ if (typeId == TDataType<TInterval>::Id) {
+ if (block) {
+ builder.Implementation(new TSimpleArrowUdfImpl(argBlockTypes, outputType, block.IsScalar(),
+ UnaryPreallocatedExecImpl<i64, TSignedResult, IntervalCore>, builder, TString(name), arrow::compute::NullHandling::INTERSECTION));
+ } else {
+ builder.Implementation(new TUnaryOverOptionalImpl<i64, TSignedResult, IntervalCore>());
}
+ return true;
}
- } catch (const std::exception& e) {
- builder.SetError(TStringBuf(e.what()));
- }
+ if (typeId == TDataType<TDate32>::Id || typeId == TDataType<TTzDate32>::Id) {
+ Y_ABORT_IF(block, "Block overload is not supported");
+ builder.Implementation(new TUnaryOverOptionalImpl<i32, TWResult, Date32Core>());
+ return true;
+ }
+
+ if (typeId == TDataType<TDatetime64>::Id || typeId == TDataType<TTzDatetime64>::Id) {
+ Y_ABORT_IF(block, "Block overload is not supported");
+ builder.Implementation(new TUnaryOverOptionalImpl<i64, TWResult, Datetime64Core>());
+ return true;
+ }
+
+ if (typeId == TDataType<TTimestamp64>::Id || typeId == TDataType<TTzTimestamp64>::Id) {
+ Y_ABORT_IF(block, "Block overload is not supported");
+ builder.Implementation(new TUnaryOverOptionalImpl<i64, TWResult, Timestamp64Core>());
+ return true;
+ }
+
+ if (typeId == TDataType<TInterval64>::Id) {
+ Y_ABORT_IF(block, "Block overload is not supported");
+ builder.Implementation(new TUnaryOverOptionalImpl<i64, TWResult, Interval64Core>());
+ return true;
+ }
+
+ Y_UNREACHABLE();
+ }
return true;
}
};
@@ -1770,29 +1830,106 @@ TUnboxedValue GetTimezoneName(const IValueBuilder* valueBuilder, const TUnboxedV
// To*
- BEGIN_SIMPLE_STRICT_ARROW_UDF(TToDays, i32(TAutoMap<TInterval>)) {
- Y_UNUSED(valueBuilder);
- return TUnboxedValuePod(i32(args[0].Get<i64>() / UsecondsInDay));
+template<const char* TUdfName, typename TResult, typename TWResult, i64 ScaleSeconds>
+class TToConverter : public TBoxedValue {
+public:
+ typedef bool TTypeAwareMarker;
+ static const ::NYql::NUdf::TStringRef& Name() {
+ static auto name = TStringRef(TUdfName, std::strlen(TUdfName));
+ return name;
}
- END_SIMPLE_ARROW_UDF_WITH_NULL_HANDLING(TToDays,
- (UnaryPreallocatedExecImpl<i64, i32, [] (i64 arg) { return i32(arg / UsecondsInDay); }>),
- arrow::compute::NullHandling::INTERSECTION);
- BEGIN_SIMPLE_STRICT_ARROW_UDF(TToHours, i32(TAutoMap<TInterval>)) {
- Y_UNUSED(valueBuilder);
- return TUnboxedValuePod(i32(args[0].Get<i64>() / UsecondsInHour));
+ static bool DeclareSignature(
+ const TStringRef& name,
+ TType* userType,
+ IFunctionTypeInfoBuilder& builder,
+ bool typesOnly)
+ {
+ if (Name() != name) {
+ return false;
+ }
+
+ if (!userType) {
+ builder.SetError("User type is missing");
+ return true;
+ }
+
+ builder.UserType(userType);
+
+ const auto typeInfoHelper = builder.TypeInfoHelper();
+ TTupleTypeInspector tuple(*typeInfoHelper, userType);
+ Y_ENSURE(tuple, "Tuple with args and options tuples expected");
+ Y_ENSURE(tuple.GetElementsCount() > 0,
+ "Tuple has to contain positional arguments");
+
+ TTupleTypeInspector argsTuple(*typeInfoHelper, tuple.GetElementType(0));
+ Y_ENSURE(argsTuple, "Tuple with args expected");
+ if (argsTuple.GetElementsCount() != 1) {
+ builder.SetError("Single argument expected");
+ return true;
+ }
+
+ auto argType = argsTuple.GetElementType(0);
+
+ if (const auto optType = TOptionalTypeInspector(*typeInfoHelper, argType)) {
+ argType = optType.GetItemType();
+ }
+
+ TDataTypeInspector data(*typeInfoHelper, argType);
+ if (!data) {
+ SetInvalidTypeError(builder, typeInfoHelper, argType);
+ return true;
+ }
+
+ const auto features = NUdf::GetDataTypeInfo(NUdf::GetDataSlot(data.GetTypeId())).Features;
+ if (features & NUdf::TimeIntervalType) {
+ if (features & NUdf::BigDateType) {
+ BuildSignature<TInterval64, TWResult>(builder, typesOnly);
+ } else {
+ BuildSignature<TInterval, TResult>(builder, typesOnly);
+ }
+ return true;
+ }
+ SetInvalidTypeError(builder, typeInfoHelper, argType);
+ return true;
+ }
+private:
+ class TImpl : public TBoxedValue {
+ public:
+ TUnboxedValue Run(const IValueBuilder* valueBuilder, const TUnboxedValuePod* args) const final {
+ try {
+ Y_UNUSED(valueBuilder);
+ return TUnboxedValuePod(i64(args[0].Get<i64>() / ScaleSeconds));
+ } catch (const std::exception& e) {
+ TStringBuilder sb;
+ sb << CurrentExceptionMessage();
+ sb << Endl << "[" << TStringBuf(Name()) << "]" ;
+ UdfTerminate(sb.c_str());
+ }
+ }
+
+ };
+
+ static void SetInvalidTypeError(NUdf::IFunctionTypeInfoBuilder& builder,
+ ITypeInfoHelper::TPtr typeInfoHelper, const TType* argType)
+ {
+ ::TStringBuilder sb;
+ sb << "Invalid argument type: got ";
+ TTypePrinter(*typeInfoHelper, argType).Out(sb.Out);
+ sb << ", but Interval or Interval64 expected";
+ builder.SetError(sb);
}
- END_SIMPLE_ARROW_UDF_WITH_NULL_HANDLING(TToHours,
- (UnaryPreallocatedExecImpl<i64, i32, [] (i64 arg) { return i32(arg / UsecondsInHour); }>),
- arrow::compute::NullHandling::INTERSECTION);
- BEGIN_SIMPLE_STRICT_ARROW_UDF(TToMinutes, i32(TAutoMap<TInterval>)) {
- Y_UNUSED(valueBuilder);
- return TUnboxedValuePod(i32(args[0].Get<i64>() / UsecondsInMinute));
+ template<typename TInput, typename TOutput>
+ static void BuildSignature(NUdf::IFunctionTypeInfoBuilder& builder, bool typesOnly) {
+ builder.Returns<TOutput>();
+ builder.Args()->Add<TAutoMap<TInput>>();
+ builder.IsStrict();
+ if (!typesOnly) {
+ builder.Implementation(new TImpl());
+ }
}
- END_SIMPLE_ARROW_UDF_WITH_NULL_HANDLING(TToMinutes,
- (UnaryPreallocatedExecImpl<i64, i32, [] (i64 arg) { return i32(arg / UsecondsInMinute); }>),
- arrow::compute::NullHandling::INTERSECTION);
+};
// StartOf*
@@ -3298,9 +3435,9 @@ private:
TInterval64FromMilliseconds,
TInterval64FromMicroseconds,
- TToDays,
- TToHours,
- TToMinutes,
+ TToConverter<ToDaysUDF, i32, i32, UsecondsInDay>,
+ TToConverter<ToHoursUDF, i32, i64, UsecondsInHour>,
+ TToConverter<ToMinutesUDF, i32, i64, UsecondsInMinute>,
TBoundaryOf<StartOfYearUDF, SimpleDatetimeToDatetimeUdf<TMResourceName, StartOfYear<TTMStorage>>,
SimpleDatetimeToDatetimeUdf<TM64ResourceName, StartOfYear<TTM64Storage>>>,
@@ -3333,9 +3470,9 @@ private:
TBoundaryOfInterval<EndOfUDF, SimpleDatetimeToIntervalUdf<TMResourceName, EndOf<TTMStorage>>,
SimpleDatetimeToIntervalUdf<TM64ResourceName, EndOf<TTM64Storage>>>,
- TToUnits<ToSecondsUDF, ui32, 1>,
- TToUnits<ToMillisecondsUDF, ui64, 1000>,
- TToUnits<ToMicrosecondsUDF, ui64, 1000000>,
+ TToUnits<ToSecondsUDF, ui32, i64, 1>,
+ TToUnits<ToMillisecondsUDF, ui64, i64, 1000>,
+ TToUnits<ToMicrosecondsUDF, ui64, i64, 1000000>,
TFormat,
TParse<ParseUDF, TMResourceName>,
diff --git a/yql/essentials/udfs/common/datetime2/test_bigdates/canondata/result.json b/yql/essentials/udfs/common/datetime2/test_bigdates/canondata/result.json
index 79e742fc9f..c88d3bdf2a 100644
--- a/yql/essentials/udfs/common/datetime2/test_bigdates/canondata/result.json
+++ b/yql/essentials/udfs/common/datetime2/test_bigdates/canondata/result.json
@@ -44,6 +44,11 @@
"uri": "file://test.test_StartOf_/results.txt"
}
],
+ "test.test[To]": [
+ {
+ "uri": "file://test.test_To_/results.txt"
+ }
+ ],
"test.test[UpdateTz]": [
{
"uri": "file://test.test_UpdateTz_/results.txt"
diff --git a/yql/essentials/udfs/common/datetime2/test_bigdates/canondata/test.test_To_/results.txt b/yql/essentials/udfs/common/datetime2/test_bigdates/canondata/test.test_To_/results.txt
new file mode 100644
index 0000000000..2c6a80c75a
--- /dev/null
+++ b/yql/essentials/udfs/common/datetime2/test_bigdates/canondata/test.test_To_/results.txt
@@ -0,0 +1,826 @@
+[
+ {
+ "Write" = [
+ {
+ "Type" = [
+ "ListType";
+ [
+ "StructType";
+ [
+ [
+ "interval64_to_days";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int32"
+ ]
+ ]
+ ];
+ [
+ "interval64_to_hours";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "interval64_to_minutes";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "interval64_to_seconds";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "interval64_to_msec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "interval64_to_usec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "date32_to_seconds";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "datetime64_to_seconds";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "timestamp64_to_seconds";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "tzdate32_to_seconds";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "tzdatetime64_to_seconds";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "tztimestamp64_to_seconds";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "date32_to_msec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "datetime64_to_msec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "timestamp64_to_msec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "tzdate32_to_msec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "tzdatetime64_to_msec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "tztimestamp64_to_msec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "date32_to_usec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "datetime64_to_usec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "timestamp64_to_usec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "tzdate32_to_usec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "tzdatetime64_to_usec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ];
+ [
+ "tztimestamp64_to_usec";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Int64"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ];
+ "Data" = [
+ [
+ [
+ "-106751616"
+ ];
+ [
+ "-2562038807"
+ ];
+ [
+ "-153722328479"
+ ];
+ [
+ "-9223339708799"
+ ];
+ [
+ "-9223339708799000"
+ ];
+ [
+ "-9223339708799000000"
+ ];
+ [
+ "-4611669897600"
+ ];
+ [
+ "-4611669897600"
+ ];
+ [
+ "-4611669897600"
+ ];
+ [
+ "-4611669897600"
+ ];
+ [
+ "-4611669897600"
+ ];
+ [
+ "-4611669897600"
+ ];
+ [
+ "-4611669897600000"
+ ];
+ [
+ "-4611669897600000"
+ ];
+ [
+ "-4611669897600000"
+ ];
+ [
+ "-4611669897600000"
+ ];
+ [
+ "-4611669897600000"
+ ];
+ [
+ "-4611669897600000"
+ ];
+ [
+ "-4611669897600000000"
+ ];
+ [
+ "-4611669897600000000"
+ ];
+ [
+ "-4611669897600000000"
+ ];
+ [
+ "-4611669897600000000"
+ ];
+ [
+ "-4611669897600000000"
+ ];
+ [
+ "-4611669897600000000"
+ ]
+ ];
+ [
+ [
+ "-53375809"
+ ];
+ [
+ "-1281019416"
+ ];
+ [
+ "-76861164960"
+ ];
+ [
+ "-4611669897600"
+ ];
+ [
+ "-4611669897600000"
+ ];
+ [
+ "-4611669897600000000"
+ ];
+ [
+ "-62167219200"
+ ];
+ [
+ "-62167132801"
+ ];
+ [
+ "-62167132800"
+ ];
+ [
+ "-62167305600"
+ ];
+ [
+ "-62167141818"
+ ];
+ [
+ "-62167141817"
+ ];
+ [
+ "-62167219200000"
+ ];
+ [
+ "-62167132801000"
+ ];
+ [
+ "-62167132800000"
+ ];
+ [
+ "-62167305600000"
+ ];
+ [
+ "-62167141818000"
+ ];
+ [
+ "-62167141817000"
+ ];
+ [
+ "-62167219200000000"
+ ];
+ [
+ "-62167132801000000"
+ ];
+ [
+ "-62167132800000001"
+ ];
+ [
+ "-62167305600000000"
+ ];
+ [
+ "-62167141818000000"
+ ];
+ [
+ "-62167141817000001"
+ ]
+ ];
+ [
+ [
+ "-1"
+ ];
+ [
+ "-24"
+ ];
+ [
+ "-1440"
+ ];
+ [
+ "-86400"
+ ];
+ [
+ "-86400000"
+ ];
+ [
+ "-86400000000"
+ ];
+ [
+ "-62135596800"
+ ];
+ [
+ "-62135596800"
+ ];
+ [
+ "-62135596800"
+ ];
+ [
+ "-62135683200"
+ ];
+ [
+ "-62135605817"
+ ];
+ [
+ "-62135605817"
+ ];
+ [
+ "-62135596800000"
+ ];
+ [
+ "-62135596800000"
+ ];
+ [
+ "-62135596800000"
+ ];
+ [
+ "-62135683200000"
+ ];
+ [
+ "-62135605817000"
+ ];
+ [
+ "-62135605817000"
+ ];
+ [
+ "-62135596800000000"
+ ];
+ [
+ "-62135596800000000"
+ ];
+ [
+ "-62135596800000000"
+ ];
+ [
+ "-62135683200000000"
+ ];
+ [
+ "-62135605817000000"
+ ];
+ [
+ "-62135605817000000"
+ ]
+ ];
+ [
+ #;
+ #;
+ #;
+ #;
+ #;
+ #;
+ [
+ "-86400"
+ ];
+ [
+ "-1"
+ ];
+ [
+ "0"
+ ];
+ [
+ "-172800"
+ ];
+ [
+ "-10801"
+ ];
+ [
+ "-10800"
+ ];
+ [
+ "-86400000"
+ ];
+ [
+ "-1000"
+ ];
+ [
+ "0"
+ ];
+ [
+ "-172800000"
+ ];
+ [
+ "-10801000"
+ ];
+ [
+ "-10800000"
+ ];
+ [
+ "-86400000000"
+ ];
+ [
+ "-1000000"
+ ];
+ [
+ "-1"
+ ];
+ [
+ "-172800000000"
+ ];
+ [
+ "-10801000000"
+ ];
+ [
+ "-10800000001"
+ ]
+ ];
+ [
+ [
+ "1"
+ ];
+ [
+ "24"
+ ];
+ [
+ "1440"
+ ];
+ [
+ "86400"
+ ];
+ [
+ "86400000"
+ ];
+ [
+ "86400000000"
+ ];
+ [
+ "0"
+ ];
+ [
+ "0"
+ ];
+ [
+ "0"
+ ];
+ [
+ "-86400"
+ ];
+ [
+ "-10800"
+ ];
+ [
+ "-10800"
+ ];
+ [
+ "0"
+ ];
+ [
+ "0"
+ ];
+ [
+ "0"
+ ];
+ [
+ "-86400000"
+ ];
+ [
+ "-10800000"
+ ];
+ [
+ "-10800000"
+ ];
+ [
+ "0"
+ ];
+ [
+ "0"
+ ];
+ [
+ "0"
+ ];
+ [
+ "-86400000000"
+ ];
+ [
+ "-10800000000"
+ ];
+ [
+ "-10800000000"
+ ]
+ ];
+ [
+ #;
+ #;
+ #;
+ #;
+ #;
+ #;
+ [
+ "1735689600"
+ ];
+ [
+ "1735693323"
+ ];
+ [
+ "1735693323"
+ ];
+ [
+ "1735603200"
+ ];
+ [
+ "1735693323"
+ ];
+ [
+ "1735693323"
+ ];
+ [
+ "1735689600000"
+ ];
+ [
+ "1735693323000"
+ ];
+ [
+ "1735693323456"
+ ];
+ [
+ "1735603200000"
+ ];
+ [
+ "1735693323000"
+ ];
+ [
+ "1735693323456"
+ ];
+ [
+ "1735689600000000"
+ ];
+ [
+ "1735693323000000"
+ ];
+ [
+ "1735693323456789"
+ ];
+ [
+ "1735603200000000"
+ ];
+ [
+ "1735693323000000"
+ ];
+ [
+ "1735693323456789"
+ ]
+ ];
+ [
+ [
+ "53375807"
+ ];
+ [
+ "1281019391"
+ ];
+ [
+ "76861163519"
+ ];
+ [
+ "4611669811199"
+ ];
+ [
+ "4611669811199000"
+ ];
+ [
+ "4611669811199000000"
+ ];
+ [
+ "4291747200"
+ ];
+ [
+ "4291747200"
+ ];
+ [
+ "4291747200"
+ ];
+ [
+ "4291660800"
+ ];
+ [
+ "4291747200"
+ ];
+ [
+ "4291747200"
+ ];
+ [
+ "4291747200000"
+ ];
+ [
+ "4291747200000"
+ ];
+ [
+ "4291747200000"
+ ];
+ [
+ "4291660800000"
+ ];
+ [
+ "4291747200000"
+ ];
+ [
+ "4291747200000"
+ ];
+ [
+ "4291747200000000"
+ ];
+ [
+ "4291747200000000"
+ ];
+ [
+ "4291747200000000"
+ ];
+ [
+ "4291660800000000"
+ ];
+ [
+ "4291747200000000"
+ ];
+ [
+ "4291747200000000"
+ ]
+ ];
+ [
+ [
+ "106751616"
+ ];
+ [
+ "2562038807"
+ ];
+ [
+ "153722328479"
+ ];
+ [
+ "9223339708799"
+ ];
+ [
+ "9223339708799000"
+ ];
+ [
+ "9223339708799000000"
+ ];
+ [
+ "4611669724800"
+ ];
+ [
+ "4611669811199"
+ ];
+ [
+ "4611669811199"
+ ];
+ [
+ "4611669638400"
+ ];
+ [
+ "4611669811199"
+ ];
+ [
+ "4611669811199"
+ ];
+ [
+ "4611669724800000"
+ ];
+ [
+ "4611669811199000"
+ ];
+ [
+ "4611669811199999"
+ ];
+ [
+ "4611669638400000"
+ ];
+ [
+ "4611669811199000"
+ ];
+ [
+ "4611669811199999"
+ ];
+ [
+ "4611669724800000000"
+ ];
+ [
+ "4611669811199000000"
+ ];
+ [
+ "4611669811199999999"
+ ];
+ [
+ "4611669638400000000"
+ ];
+ [
+ "4611669811199000000"
+ ];
+ [
+ "4611669811199999999"
+ ]
+ ]
+ ]
+ }
+ ]
+ }
+] \ No newline at end of file
diff --git a/yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.cfg b/yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.cfg
new file mode 100644
index 0000000000..31260d619b
--- /dev/null
+++ b/yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.cfg
@@ -0,0 +1 @@
+in plato.Input To.in
diff --git a/yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.in b/yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.in
new file mode 100644
index 0000000000..1097a51534
--- /dev/null
+++ b/yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.in
@@ -0,0 +1,72 @@
+{
+ "fdate32"="-144169-01-01";
+ "fdatetime64"="-144169-01-01T00:00:00Z";
+ "ftimestamp64"="-144169-01-01T00:00:00.000000Z";
+ "ftzdate32"="-144169-01-02,Europe/Moscow";
+ "ftzdatetime64"="-144169-01-01T02:30:17,Europe/Moscow";
+ "ftztimestamp64"="-144169-01-01T02:30:17.000000,Europe/Moscow";
+ "finterval64"="-PT9223339708799S";
+};
+{
+ "fdate32"="-1-01-01";
+ "fdatetime64"="-1-01-01T23:59:59Z";
+ "ftimestamp64"="-1-01-01T23:59:59.999999Z";
+ "ftzdate32"="-1-01-01,Europe/Moscow";
+ "ftzdatetime64"="-1-01-01T23:59:59,Europe/Moscow";
+ "ftztimestamp64"="-1-01-01T23:59:59.999999,Europe/Moscow";
+ "finterval64"="-PT4611669897600S";
+};
+{
+ "fdate32"="1-01-01";
+ "fdatetime64"="1-01-01T00:00:00Z";
+ "ftimestamp64"="1-01-01T00:00:00.000000Z";
+ "ftzdate32"="1-01-01,Europe/Moscow";
+ "ftzdatetime64"="1-01-01T00:00:00,Europe/Moscow";
+ "ftztimestamp64"="1-01-01T00:00:00.000000,Europe/Moscow";
+ "finterval64"="-P1D";
+};
+{
+ "fdate32"="1969-12-31";
+ "fdatetime64"="1969-12-31T23:59:59Z";
+ "ftimestamp64"="1969-12-31T23:59:59.999999Z";
+ "ftzdate32"="1969-12-31,Europe/Moscow";
+ "ftzdatetime64"="1969-12-31T23:59:59,Europe/Moscow";
+ "ftztimestamp64"="1969-12-31T23:59:59.999999,Europe/Moscow";
+ "finterval64"="P1W1DT1H1M1.000001S";
+};
+{
+ "fdate32"="1970-01-01";
+ "fdatetime64"="1970-01-01T00:00:00Z";
+ "ftimestamp64"="1970-01-01T00:00:00.000000Z";
+ "ftzdate32"="1970-01-01,Europe/Moscow";
+ "ftzdatetime64"="1970-01-01T00:00:00,Europe/Moscow";
+ "ftztimestamp64"="1970-01-01T00:00:00.000000,Europe/Moscow";
+ "finterval64"="P1D";
+};
+{
+ "fdate32"="2025-01-01";
+ "fdatetime64"="2025-01-01T01:02:03Z";
+ "ftimestamp64"="2025-01-01T01:02:03.456789Z";
+ "ftzdate32"="2025-01-01,Europe/Moscow";
+ "ftzdatetime64"="2025-01-01T04:02:03,Europe/Moscow";
+ "ftztimestamp64"="2025-01-01T04:02:03.456789,Europe/Moscow";
+ "finterval64"="P1W2DT3H4M5.678912S";
+};
+{
+ "fdate32"="2106-01-01";
+ "fdatetime64"="2106-01-01T00:00:00Z";
+ "ftimestamp64"="2106-01-01T00:00:00.000000Z";
+ "ftzdate32"="2106-01-01,Europe/Moscow";
+ "ftzdatetime64"="2106-01-01T03:00:00,Europe/Moscow";
+ "ftztimestamp64"="2106-01-01T03:00:00.000000,Europe/Moscow";
+ "finterval64"="PT4611669811199S";
+};
+{
+ "fdate32"="148107-12-31";
+ "fdatetime64"="148107-12-31T23:59:59Z";
+ "ftimestamp64"="148107-12-31T23:59:59.999999Z";
+ "ftzdate32"="148107-12-31,Europe/Moscow";
+ "ftzdatetime64"="148108-01-01T02:59:59,Europe/Moscow";
+ "ftztimestamp64"="148108-01-01T02:59:59.999999,Europe/Moscow";
+ "finterval64"="PT9223339708799S";
+};
diff --git a/yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.in.attr b/yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.in.attr
new file mode 100644
index 0000000000..53b63c62f6
--- /dev/null
+++ b/yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.in.attr
@@ -0,0 +1,59 @@
+{
+ "_yql_row_spec" = {
+ "Type" = [
+ "StructType";
+ [
+ [
+ "fdate32";
+ [
+ "DataType";
+ "String"
+ ]
+ ];
+ [
+ "fdatetime64";
+ [
+ "DataType";
+ "String"
+ ]
+ ];
+ [
+ "ftimestamp64";
+ [
+ "DataType";
+ "String"
+ ]
+ ];
+ [
+ "ftzdate32";
+ [
+ "DataType";
+ "String"
+ ]
+ ];
+ [
+ "ftzdatetime64";
+ [
+ "DataType";
+ "String"
+ ]
+ ];
+ [
+ "ftztimestamp64";
+ [
+ "DataType";
+ "String"
+ ]
+ ];
+ [
+ "finterval64";
+ [
+ "DataType";
+ "String"
+ ]
+ ];
+ ]
+ ]
+ }
+}
+
diff --git a/yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.sql b/yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.sql
new file mode 100644
index 0000000000..07c15913d3
--- /dev/null
+++ b/yql/essentials/udfs/common/datetime2/test_bigdates/cases/To.sql
@@ -0,0 +1,40 @@
+/* syntax version 1 */
+SELECT
+ DateTime::ToDays(finterval64) as interval64_to_days,
+ DateTime::ToHours(finterval64) as interval64_to_hours,
+ DateTime::ToMinutes(finterval64) as interval64_to_minutes,
+ DateTime::ToSeconds(finterval64) as interval64_to_seconds,
+ DateTime::ToMilliseconds(finterval64) as interval64_to_msec,
+ DateTime::ToMicroseconds(finterval64) as interval64_to_usec,
+
+ DateTime::ToSeconds(fdate32) as date32_to_seconds,
+ DateTime::ToSeconds(fdatetime64) as datetime64_to_seconds,
+ DateTime::ToSeconds(ftimestamp64) as timestamp64_to_seconds,
+ DateTime::ToSeconds(ftzdate32) as tzdate32_to_seconds,
+ DateTime::ToSeconds(ftzdatetime64) as tzdatetime64_to_seconds,
+ DateTime::ToSeconds(ftztimestamp64) as tztimestamp64_to_seconds,
+
+ DateTime::ToMilliseconds(fdate32) as date32_to_msec,
+ DateTime::ToMilliseconds(fdatetime64) as datetime64_to_msec,
+ DateTime::ToMilliseconds(ftimestamp64) as timestamp64_to_msec,
+ DateTime::ToMilliseconds(ftzdate32) as tzdate32_to_msec,
+ DateTime::ToMilliseconds(ftzdatetime64) as tzdatetime64_to_msec,
+ DateTime::ToMilliseconds(ftztimestamp64) as tztimestamp64_to_msec,
+
+ DateTime::ToMicroseconds(fdate32) as date32_to_usec,
+ DateTime::ToMicroseconds(fdatetime64) as datetime64_to_usec,
+ DateTime::ToMicroseconds(ftimestamp64) as timestamp64_to_usec,
+ DateTime::ToMicroseconds(ftzdate32) as tzdate32_to_usec,
+ DateTime::ToMicroseconds(ftzdatetime64) as tzdatetime64_to_usec,
+ DateTime::ToMicroseconds(ftztimestamp64) as tztimestamp64_to_usec,
+FROM (
+ SELECT
+ CAST(fdate32 as Date32) as fdate32,
+ CAST(fdatetime64 as Datetime64) as fdatetime64,
+ CAST(ftimestamp64 as Timestamp64) as ftimestamp64,
+ CAST(finterval64 as Interval64) as finterval64,
+ CAST(ftzdate32 as TzDate32) as ftzdate32,
+ CAST(ftzdatetime64 as TzDatetime64) as ftzdatetime64,
+ CAST(ftztimestamp64 as TzTimestamp64) as ftztimestamp64,
+ from Input
+);
diff --git a/yql/essentials/udfs/common/python/bindings/py_callable_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_callable_ut.cpp
index 1c58d7b371..36cc13a1da 100644
--- a/yql/essentials/udfs/common/python/bindings/py_callable_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_callable_ut.cpp
@@ -1,4 +1,4 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <library/cpp/testing/unittest/registar.h>
diff --git a/yql/essentials/udfs/common/python/bindings/py_cast.cpp b/yql/essentials/udfs/common/python/bindings/py_cast.cpp
index 078239962e..ac44ac42e5 100644
--- a/yql/essentials/udfs/common/python/bindings/py_cast.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_cast.cpp
@@ -22,6 +22,7 @@
#include <library/cpp/containers/stack_vector/stack_vec.h>
+#include <util/generic/scope.h>
#include <util/string/join.h>
#include <util/string/builder.h>
@@ -252,7 +253,7 @@
template <> \
bool TryPyCast(PyObject* value, Type& result) { \
if (PyUnicode_Check(value)) { \
- const TPyObjectPtr utf8(PyUnicode_AsUTF8String(value)); \
+ const TPyObjectPtr utf8(AsUtf8StringOrThrow(value)); \
char* str = nullptr; \
Py_ssize_t size = 0; \
int rc = PyBytes_AsStringAndSize(utf8.Get(), &str, &size); \
@@ -279,6 +280,22 @@ namespace NPython {
using namespace NKikimr;
+namespace {
+
+NPython::TPyObjectPtr AsUtf8StringOrThrow(PyObject* obj) {
+ auto* utf8String = PyUnicode_AsUTF8String(obj);
+ if (!utf8String) {
+ Y_ENSURE(PyErr_Occurred());
+ Y_DEFER {
+ PyErr_Clear();
+ };
+ throw yexception() << "Failed to convert the string to UTF-8 format. Original message is:\n" << GetLastErrorAsString() << "\n";
+ }
+ return NPython::TPyObjectPtr(utf8String);
+}
+
+} // namespace
+
inline void ThrowCastTypeException(PyObject* value, TStringBuf toType) {
throw yexception() << "Can't cast object '" << Py_TYPE(value)->tp_name << "' to " << toType
<< "; Object repr: " << PyObjectRepr(value);
@@ -548,7 +565,7 @@ NUdf::TUnboxedValue FromPyData(
case NUdf::TDataType<NUdf::TUtf8>::Id:
case NUdf::TDataType<NUdf::TJson>::Id:
if (PyUnicode_Check(value)) {
- const TPyObjectPtr uif8(PyUnicode_AsUTF8String(value));
+ const TPyObjectPtr uif8(AsUtf8StringOrThrow(value));
return ctx->ValueBuilder->NewString(PyCast<NUdf::TStringRef>(uif8.Get()));
}
throw yexception() << "Python object " << PyObjectRepr(value) << " has invalid value for unicode";
@@ -557,7 +574,7 @@ NUdf::TUnboxedValue FromPyData(
case NUdf::TDataType<NUdf::TJson>::Id:
case NUdf::TDataType<NUdf::TUtf8>::Id: {
if (PyUnicode_Check(value)) {
- const TPyObjectPtr utf8(PyUnicode_AsUTF8String(value));
+ const TPyObjectPtr utf8(AsUtf8StringOrThrow(value));
return ctx->ValueBuilder->NewString(PyCast<NUdf::TStringRef>(utf8.Get()));
}
diff --git a/yql/essentials/udfs/common/python/bindings/py_cast_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_cast_ut.cpp
index 47f65ab6fa..d4ee90278c 100644
--- a/yql/essentials/udfs/common/python/bindings/py_cast_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_cast_ut.cpp
@@ -1,9 +1,39 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <library/cpp/testing/unittest/registar.h>
+#include <util/string/strip.h>
using namespace NPython;
+namespace {
+template <typename TType>
+void TestBadUtf8Encode() {
+#if PY_MAJOR_VERSION == 2
+ // In Python 2, strings can encode single surrogate pairs, so this issue does not occur there.
+ return;
+#endif // PY_MAJOR_VERSION == 2
+
+ TPythonTestEngine engine;
+
+ constexpr char programToRun[] = R"(
+def Test():
+ return "\uDC00"
+)";
+ constexpr char expectedError[] = R"(
+Failed to convert the string to UTF-8 format. Original message is:
+UnicodeEncodeError: 'utf-8' codec can't encode character '\udc00' in position 0: surrogates not allowed
+)";
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ engine.ToMiniKQL<TType>(
+ StripString(TString(programToRun)),
+ [](const NUdf::TUnboxedValuePod& value) {
+ Y_UNUSED(value);
+ }),
+ yexception, StripString(TString(expectedError)));
+}
+} // namespace
+
Y_UNIT_TEST_SUITE(TPyCastTest) {
Y_UNIT_TEST(FromPyStrToInt) {
TPythonTestEngine engine;
@@ -87,4 +117,11 @@ Y_UNIT_TEST_SUITE(TPyCastTest) {
yexception, "Cast error object " RETVAL " to Long");
}
-}
+ Y_UNIT_TEST(BadFromPythonUtf8) {
+ TestBadUtf8Encode<NUdf::TUtf8>();
+ }
+
+ Y_UNIT_TEST(BadFromPythonJson) {
+ TestBadUtf8Encode<NUdf::TJson>();
+ }
+} // Y_UNIT_TEST_SUITE(TPyCastTest)
diff --git a/yql/essentials/udfs/common/python/bindings/py_decimal_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_decimal_ut.cpp
index 8388c110f3..3f0298013a 100644
--- a/yql/essentials/udfs/common/python/bindings/py_decimal_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_decimal_ut.cpp
@@ -1,4 +1,4 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <library/cpp/testing/unittest/registar.h>
diff --git a/yql/essentials/udfs/common/python/bindings/py_dict_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_dict_ut.cpp
index 9ac9627ebb..edb3d36e8c 100644
--- a/yql/essentials/udfs/common/python/bindings/py_dict_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_dict_ut.cpp
@@ -1,4 +1,4 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <yql/essentials/public/udf/udf_ut_helpers.h>
diff --git a/yql/essentials/udfs/common/python/bindings/py_list_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_list_ut.cpp
index f16165fc54..b2e9a640d4 100644
--- a/yql/essentials/udfs/common/python/bindings/py_list_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_list_ut.cpp
@@ -1,4 +1,4 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <yql/essentials/public/udf/udf_ut_helpers.h>
diff --git a/yql/essentials/udfs/common/python/bindings/py_number_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_number_ut.cpp
index c55e25891d..35c94d5e8e 100644
--- a/yql/essentials/udfs/common/python/bindings/py_number_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_number_ut.cpp
@@ -1,4 +1,4 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <library/cpp/testing/unittest/registar.h>
diff --git a/yql/essentials/udfs/common/python/bindings/py_optional_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_optional_ut.cpp
index d13ea65da6..4cc45f1184 100644
--- a/yql/essentials/udfs/common/python/bindings/py_optional_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_optional_ut.cpp
@@ -1,4 +1,4 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <library/cpp/testing/unittest/registar.h>
diff --git a/yql/essentials/udfs/common/python/bindings/py_resource_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_resource_ut.cpp
index aaa9899c4f..25b43cbf6a 100644
--- a/yql/essentials/udfs/common/python/bindings/py_resource_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_resource_ut.cpp
@@ -1,4 +1,4 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <library/cpp/testing/unittest/registar.h>
diff --git a/yql/essentials/udfs/common/python/bindings/py_stream_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_stream_ut.cpp
index 4a24dd1a13..4a36f7b8f3 100644
--- a/yql/essentials/udfs/common/python/bindings/py_stream_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_stream_ut.cpp
@@ -1,4 +1,4 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <library/cpp/testing/unittest/registar.h>
diff --git a/yql/essentials/udfs/common/python/bindings/py_string_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_string_ut.cpp
index 444b7b0c5b..b1f5a13786 100644
--- a/yql/essentials/udfs/common/python/bindings/py_string_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_string_ut.cpp
@@ -1,4 +1,4 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <library/cpp/testing/unittest/registar.h>
diff --git a/yql/essentials/udfs/common/python/bindings/py_struct_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_struct_ut.cpp
index a97507f549..9b232d99a3 100644
--- a/yql/essentials/udfs/common/python/bindings/py_struct_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_struct_ut.cpp
@@ -1,4 +1,4 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <library/cpp/testing/unittest/registar.h>
diff --git a/yql/essentials/udfs/common/python/bindings/ut3/py_test_engine.h b/yql/essentials/udfs/common/python/bindings/py_test_engine.h
index a36e19fa32..c86febf779 100644
--- a/yql/essentials/udfs/common/python/bindings/ut3/py_test_engine.h
+++ b/yql/essentials/udfs/common/python/bindings/py_test_engine.h
@@ -27,6 +27,8 @@ struct TPyInitializer {
PrepareYqlModule();
Py_Initialize();
InitYqlModule(NYql::NUdf::EPythonFlavor::Arcadia);
+ const auto rc = PyRun_SimpleString(NYql::NUdf::STANDART_STREAM_PROXY_INJECTION_SCRIPT);
+ Y_ENSURE(rc >= 0, "Can't setup module");
}
~TPyInitializer() {
TermYqlModule();
diff --git a/yql/essentials/udfs/common/python/bindings/py_tuple_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_tuple_ut.cpp
index a6b9b6cc3e..f465f0ebb6 100644
--- a/yql/essentials/udfs/common/python/bindings/py_tuple_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_tuple_ut.cpp
@@ -1,4 +1,4 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <library/cpp/testing/unittest/registar.h>
diff --git a/yql/essentials/udfs/common/python/bindings/py_tzdate_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_tzdate_ut.cpp
index e9f5971c78..08b6b78b16 100644
--- a/yql/essentials/udfs/common/python/bindings/py_tzdate_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_tzdate_ut.cpp
@@ -1,5 +1,5 @@
#include "py_variant.h"
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <yql/essentials/minikql/mkql_type_ops.h>
#include <library/cpp/testing/unittest/registar.h>
diff --git a/yql/essentials/udfs/common/python/bindings/py_variant_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_variant_ut.cpp
index 77ab9bc6e8..d792449d82 100644
--- a/yql/essentials/udfs/common/python/bindings/py_variant_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_variant_ut.cpp
@@ -1,5 +1,5 @@
#include "py_variant.h"
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <library/cpp/testing/unittest/registar.h>
diff --git a/yql/essentials/udfs/common/python/bindings/py_void_ut.cpp b/yql/essentials/udfs/common/python/bindings/py_void_ut.cpp
index 7fbeca2043..e6e8a72768 100644
--- a/yql/essentials/udfs/common/python/bindings/py_void_ut.cpp
+++ b/yql/essentials/udfs/common/python/bindings/py_void_ut.cpp
@@ -1,4 +1,4 @@
-#include "ut3/py_test_engine.h"
+#include "py_test_engine.h"
#include <library/cpp/testing/unittest/registar.h>
diff --git a/yql/essentials/udfs/common/python/bindings/ut2/ya.make b/yql/essentials/udfs/common/python/bindings/ut2/ya.make
new file mode 100644
index 0000000000..b289e188bb
--- /dev/null
+++ b/yql/essentials/udfs/common/python/bindings/ut2/ya.make
@@ -0,0 +1,9 @@
+IF (OS_LINUX)
+ IF (NOT WITH_VALGRIND)
+ UNITTEST_FOR(yql/essentials/udfs/common/python/bindings)
+
+ INCLUDE(../ya.make.test.inc)
+ USE_PYTHON2()
+ END()
+ ENDIF()
+ENDIF()
diff --git a/yql/essentials/udfs/common/python/bindings/ut3/ya.make b/yql/essentials/udfs/common/python/bindings/ut3/ya.make
index cfce3cb9be..3a3c52dfbf 100644
--- a/yql/essentials/udfs/common/python/bindings/ut3/ya.make
+++ b/yql/essentials/udfs/common/python/bindings/ut3/ya.make
@@ -2,36 +2,11 @@ IF (OS_LINUX)
IF (NOT WITH_VALGRIND)
UNITTEST_FOR(yql/essentials/udfs/common/python/bindings)
- SRCS(
- py_callable_ut.cpp
- py_cast_ut.cpp
- py_dict_ut.cpp
- py_list_ut.cpp
- py_decimal_ut.cpp
- py_number_ut.cpp
- py_optional_ut.cpp
- py_resource_ut.cpp
- py_stream_ut.cpp
- py_string_ut.cpp
- py_struct_ut.cpp
- py_tuple_ut.cpp
- py_tzdate_ut.cpp
- py_utils_ut.cpp
- py_variant_ut.cpp
- py_void_ut.cpp
- )
-
- USE_PYTHON3()
-
+ INCLUDE(../ya.make.test.inc)
PEERDIR(
library/python/type_info
- yql/essentials/minikql/computation/llvm16
- yql/essentials/public/udf/service/exception_policy
- yql/essentials/sql/pg_dummy
)
-
- YQL_LAST_ABI_VERSION()
-
+ USE_PYTHON3()
END()
ENDIF()
ENDIF()
diff --git a/yql/essentials/udfs/common/python/bindings/ya.make b/yql/essentials/udfs/common/python/bindings/ya.make
index 83380ce4c4..aea3e54717 100644
--- a/yql/essentials/udfs/common/python/bindings/ya.make
+++ b/yql/essentials/udfs/common/python/bindings/ya.make
@@ -51,6 +51,7 @@ END()
IF (NOT EXPORT_CMAKE)
RECURSE_FOR_TESTS(
+ ut2
ut3
)
ENDIF()
diff --git a/yql/essentials/udfs/common/python/bindings/ya.make.test.inc b/yql/essentials/udfs/common/python/bindings/ya.make.test.inc
new file mode 100644
index 0000000000..67803ad18f
--- /dev/null
+++ b/yql/essentials/udfs/common/python/bindings/ya.make.test.inc
@@ -0,0 +1,27 @@
+SRCS(
+ py_callable_ut.cpp
+ py_cast_ut.cpp
+ py_dict_ut.cpp
+ py_list_ut.cpp
+ py_decimal_ut.cpp
+ py_number_ut.cpp
+ py_optional_ut.cpp
+ py_resource_ut.cpp
+ py_stream_ut.cpp
+ py_string_ut.cpp
+ py_struct_ut.cpp
+ py_tuple_ut.cpp
+ py_tzdate_ut.cpp
+ py_utils_ut.cpp
+ py_variant_ut.cpp
+ py_void_ut.cpp
+)
+
+PEERDIR(
+ yql/essentials/minikql/computation/llvm16
+ yql/essentials/public/udf/service/exception_policy
+ yql/essentials/sql/pg_dummy
+ yql/essentials/udfs/common/python/python_udf
+)
+
+YQL_LAST_ABI_VERSION()
diff --git a/yql/essentials/udfs/common/python/python_udf/python_udf.cpp b/yql/essentials/udfs/common/python/python_udf/python_udf.cpp
index b1739a1775..1007c75edc 100644
--- a/yql/essentials/udfs/common/python/python_udf/python_udf.cpp
+++ b/yql/essentials/udfs/common/python/python_udf/python_udf.cpp
@@ -59,59 +59,7 @@ public:
InitYqlModule(pythonFlavor, standalone);
- const auto rc = PyRun_SimpleString(R"(
-# numpy on import may find installed openblas library and load it,
-# which in turn causes it to start CPUCOUNT threads
-# with approx. 40Mb memory reserved for each thread;
-#
-# See more detailed explanation here: https://st.yandex-team.ru/STATLIBS-1715#5bfc68ecbbc039001cec572a
-#
-# Thus, we reduce negative effects as much as possible
-import os
-os.environ['OPENBLAS_NUM_THREADS'] = '1'
-
-
-# Following part allows us later to format tracebacks via sys.excepthook
-# in thread-safe manner
-import sys
-import threading
-if sys.version_info >= (3, 0):
- from io import StringIO, TextIOWrapper as SysStderrType
-else:
- from cStringIO import StringIO
- SysStderrType = file
-
-class StderrLocal(threading.local):
-
- def __init__(self):
- self.is_real_mode = True
- self.buffer = StringIO()
-
-
-class StderrProxy(object):
- def __init__(self, stderr):
- self._stderr = stderr
- self._tls = StderrLocal()
-
- def _toggle_real_mode(self):
- self._tls.is_real_mode = not self._tls.is_real_mode
- if not self._tls.is_real_mode:
- self._tls.buffer.clear()
-
- def _get_value(self):
- assert not self._tls.is_real_mode
- return self._tls.buffer.getvalue()
-
- def __getattr__(self, attr):
- target = self._stderr
- if not self._tls.is_real_mode:
- target = self._tls.buffer
-
- return getattr(target, attr)
-
-if isinstance(sys.stderr, SysStderrType):
- sys.stderr = StderrProxy(sys.stderr)
-)");
+ const auto rc = PyRun_SimpleString(STANDART_STREAM_PROXY_INJECTION_SCRIPT);
Y_ABORT_UNLESS(rc >= 0, "Can't setup module");
if (pythonFlavor == EPythonFlavor::Arcadia) {
diff --git a/yql/essentials/udfs/common/python/python_udf/python_udf.h b/yql/essentials/udfs/common/python/python_udf/python_udf.h
index 16d7da096d..83b3bb86e6 100644
--- a/yql/essentials/udfs/common/python/python_udf/python_udf.h
+++ b/yql/essentials/udfs/common/python/python_udf/python_udf.h
@@ -5,6 +5,61 @@
namespace NYql {
namespace NUdf {
+inline constexpr char STANDART_STREAM_PROXY_INJECTION_SCRIPT[] =
+R"(
+# numpy on import may find installed openblas library and load it,
+# which in turn causes it to start CPUCOUNT threads
+# with approx. 40Mb memory reserved for each thread;
+#
+# See more detailed explanation here: https://st.yandex-team.ru/STATLIBS-1715#5bfc68ecbbc039001cec572a
+#
+# Thus, we reduce negative effects as much as possible
+import os
+os.environ['OPENBLAS_NUM_THREADS'] = '1'
+
+
+# Following part allows us later to format tracebacks via sys.excepthook
+# in thread-safe manner
+import sys
+import threading
+if sys.version_info >= (3, 0):
+ from io import StringIO, TextIOWrapper as SysStderrType
+else:
+ from cStringIO import StringIO
+ SysStderrType = file
+
+class StderrLocal(threading.local):
+
+ def __init__(self):
+ self.is_real_mode = True
+ self.buffer = StringIO()
+
+
+class StderrProxy(object):
+ def __init__(self, stderr):
+ self._stderr = stderr
+ self._tls = StderrLocal()
+
+ def _toggle_real_mode(self):
+ self._tls.is_real_mode = not self._tls.is_real_mode
+ if not self._tls.is_real_mode:
+ self._tls.buffer.clear()
+
+ def _get_value(self):
+ assert not self._tls.is_real_mode
+ return self._tls.buffer.getvalue()
+
+ def __getattr__(self, attr):
+ target = self._stderr
+ if not self._tls.is_real_mode:
+ target = self._tls.buffer
+
+ return getattr(target, attr)
+
+if isinstance(sys.stderr, SysStderrType):
+ sys.stderr = StderrProxy(sys.stderr)
+)";
+
enum class EPythonFlavor {
System,
Arcadia,
diff --git a/yql/essentials/udfs/common/stat/static/stat_udf.h b/yql/essentials/udfs/common/stat/static/stat_udf.h
index f0c11a6812..36a1bad7c4 100644
--- a/yql/essentials/udfs/common/stat/static/stat_udf.h
+++ b/yql/essentials/udfs/common/stat/static/stat_udf.h
@@ -22,7 +22,7 @@ namespace {
UdfTerminate((TStringBuilder() << GetPos() << " Invalid combination of delta/K values").data());
}
- return TUnboxedValuePod(new TDigestResource(delta, K, args[0].Get<double>()));
+ return TUnboxedValuePod(new TDigestResource(delta, K, args[0].Get<double>(), true));
}
SIMPLE_STRICT_UDF(TTDigest_AddValue, TResource<DigestResourceName>(TResource<DigestResourceName>, double)) {
@@ -46,14 +46,17 @@ namespace {
SIMPLE_UDF(TTDigest_Deserialize, TResource<DigestResourceName>(char*)) {
Y_UNUSED(valueBuilder);
- return TUnboxedValuePod(new TDigestResource(TString(args[0].AsStringRef())));
+ return TUnboxedValuePod(new TDigestResource(TString(args[0].AsStringRef()), true));
}
SIMPLE_STRICT_UDF(TTDigest_Merge, TResource<DigestResourceName>(TResource<DigestResourceName>, TResource<DigestResourceName>)) {
Y_UNUSED(valueBuilder);
TDigestResource::Validate(args[0]);
TDigestResource::Validate(args[1]);
- return TUnboxedValuePod(new TDigestResource(static_cast<TDigestResource*>(args[0].AsBoxed().Get())->Get(), static_cast<TDigestResource*>(args[1].AsBoxed().Get())->Get()));
+ return TUnboxedValuePod(new TDigestResource(
+ static_cast<TDigestResource*>(args[0].AsBoxed().Get())->Get(),
+ static_cast<TDigestResource*>(args[1].AsBoxed().Get())->Get(),
+ true));
}
/*
diff --git a/yql/essentials/udfs/common/stat/test/canondata/result.json b/yql/essentials/udfs/common/stat/test/canondata/result.json
new file mode 100644
index 0000000000..44314e0309
--- /dev/null
+++ b/yql/essentials/udfs/common/stat/test/canondata/result.json
@@ -0,0 +1,7 @@
+{
+ "test.test[nan]": [
+ {
+ "uri": "file://test.test_nan_/results.txt"
+ }
+ ]
+}
diff --git a/yql/essentials/udfs/common/stat/test/canondata/test.test_nan_/results.txt b/yql/essentials/udfs/common/stat/test/canondata/test.test_nan_/results.txt
new file mode 100644
index 0000000000..1ae063d52a
--- /dev/null
+++ b/yql/essentials/udfs/common/stat/test/canondata/test.test_nan_/results.txt
@@ -0,0 +1,46 @@
+[
+ {
+ "Write" = [
+ {
+ "Type" = [
+ "ListType";
+ [
+ "StructType";
+ [
+ [
+ "column0";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Double"
+ ]
+ ]
+ ];
+ [
+ "column1";
+ [
+ "OptionalType";
+ [
+ "DataType";
+ "Double"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ];
+ "Data" = [
+ [
+ [
+ "1.1"
+ ];
+ [
+ "nan"
+ ]
+ ]
+ ]
+ }
+ ]
+ }
+] \ No newline at end of file
diff --git a/yql/essentials/udfs/common/stat/test/cases/nan.sql b/yql/essentials/udfs/common/stat/test/cases/nan.sql
new file mode 100644
index 0000000000..5ab4027b05
--- /dev/null
+++ b/yql/essentials/udfs/common/stat/test/cases/nan.sql
@@ -0,0 +1,2 @@
+select percentile(x,0.99),percentile(x,1.0)
+from (values (double("nan")),(1.1),(0.5)) as a(x)
diff --git a/yql/essentials/udfs/common/stat/test/ya.make b/yql/essentials/udfs/common/stat/test/ya.make
new file mode 100644
index 0000000000..4a14f530f1
--- /dev/null
+++ b/yql/essentials/udfs/common/stat/test/ya.make
@@ -0,0 +1,13 @@
+YQL_UDF_TEST()
+
+DEPENDS(yql/essentials/udfs/common/stat)
+
+TIMEOUT(300)
+
+SIZE(MEDIUM)
+
+IF (SANITIZER_TYPE == "memory")
+ TAG(ya:not_autocheck) # YQL-15385
+ENDIF()
+
+END()
diff --git a/yql/essentials/udfs/common/stat/ya.make b/yql/essentials/udfs/common/stat/ya.make
index 8a5538b371..d1e622b444 100644
--- a/yql/essentials/udfs/common/stat/ya.make
+++ b/yql/essentials/udfs/common/stat/ya.make
@@ -18,6 +18,7 @@ YQL_UDF_CONTRIB(stat_udf)
IF (NOT EXPORT_CMAKE)
RECURSE_FOR_TESTS(
+ test
ut
)
ENDIF()
diff --git a/yt/cpp/mapreduce/client/client.cpp b/yt/cpp/mapreduce/client/client.cpp
index 6e58903308..bf9936a64a 100644
--- a/yt/cpp/mapreduce/client/client.cpp
+++ b/yt/cpp/mapreduce/client/client.cpp
@@ -1496,8 +1496,7 @@ IClientPtr TClient::GetParentClient(bool ignoreGlobalTx)
RawClient_,
Context_,
TTransactionId(),
- ClientRetryPolicy_
- );
+ ClientRetryPolicy_);
} else {
return this;
}
@@ -1510,15 +1509,10 @@ void TClient::CheckShutdown() const
}
}
-TClientContext CreateClientContext(
- const TString& serverName,
- const TCreateClientOptions& options)
+void SetupClusterContext(
+ TClientContext& context,
+ const TString& serverName)
{
- TClientContext context;
- context.Config = options.Config_ ? options.Config_ : TConfig::Get();
- context.TvmOnly = options.TvmOnly_;
- context.ProxyAddress = options.ProxyAddress_;
-
context.ServerName = serverName;
ApplyProxyUrlAliasingRules(context.ServerName);
@@ -1531,9 +1525,8 @@ TClientContext CreateClientContext(
static constexpr char httpUrlSchema[] = "http://";
static constexpr char httpsUrlSchema[] = "https://";
- if (options.UseTLS_) {
- context.UseTLS = *options.UseTLS_;
- } else {
+
+ if (!context.UseTLS) {
context.UseTLS = context.ServerName.StartsWith(httpsUrlSchema);
}
@@ -1555,9 +1548,25 @@ TClientContext CreateClientContext(
if (context.ServerName.find(':') == TString::npos) {
context.ServerName = CreateHostNameWithPort(context.ServerName, context);
}
- if (options.TvmOnly_) {
+ if (context.TvmOnly) {
context.ServerName = Format("tvm.%v", context.ServerName);
}
+}
+
+TClientContext CreateClientContext(
+ const TString& serverName,
+ const TCreateClientOptions& options)
+{
+ TClientContext context;
+ context.Config = options.Config_ ? options.Config_ : TConfig::Get();
+ context.TvmOnly = options.TvmOnly_;
+ context.ProxyAddress = options.ProxyAddress_;
+
+ if (options.UseTLS_) {
+ context.UseTLS = *options.UseTLS_;
+ }
+
+ SetupClusterContext(context, serverName);
if (options.ProxyRole_) {
context.Config->Hosts = "hosts?role=" + *options.ProxyRole_;
diff --git a/yt/cpp/mapreduce/client/client.h b/yt/cpp/mapreduce/client/client.h
index 9cd650bd2d..ef0741044c 100644
--- a/yt/cpp/mapreduce/client/client.h
+++ b/yt/cpp/mapreduce/client/client.h
@@ -509,6 +509,10 @@ private:
////////////////////////////////////////////////////////////////////////////////
+void SetupClusterContext(
+ TClientContext& context,
+ const TString& serverName);
+
TClientContext CreateClientContext(
const TString& serverName,
const TCreateClientOptions& options);
diff --git a/yt/cpp/mapreduce/client/client_reader.cpp b/yt/cpp/mapreduce/client/client_reader.cpp
index 61369859bc..37d39345f1 100644
--- a/yt/cpp/mapreduce/client/client_reader.cpp
+++ b/yt/cpp/mapreduce/client/client_reader.cpp
@@ -69,7 +69,7 @@ TClientReader::TClientReader(
if (useFormatFromTableAttributes) {
auto transactionId2 = ReadTransaction_ ? ReadTransaction_->GetId() : ParentTransactionId_;
- auto newFormat = GetTableFormat(ClientRetryPolicy_, RawClient_, transactionId2, Path_);
+ auto newFormat = GetTableFormat(ClientRetryPolicy_, RawClient_, Context_, transactionId2, Path_);
if (newFormat) {
Format_->Config = *newFormat;
}
diff --git a/yt/cpp/mapreduce/client/operation.cpp b/yt/cpp/mapreduce/client/operation.cpp
index 76a55b436f..4d0cc07bed 100644
--- a/yt/cpp/mapreduce/client/operation.cpp
+++ b/yt/cpp/mapreduce/client/operation.cpp
@@ -220,11 +220,24 @@ TStructuredJobTableList ApplyProtobufColumnFilters(
return tableList;
}
- auto isDynamic = NRawClient::BatchTransform(
+ TVector<TRichYPath> tableListPaths;
+ for (const auto& table: tableList) {
+ Y_ABORT_UNLESS(table.RichYPath, "Cannot get path to apply column filters");
+ tableListPaths.emplace_back(*table.RichYPath);
+ }
+
+ auto isDynamic = NRawClient::RemoteClustersBatchTransform(
preparer.GetClient()->GetRawClient(),
- tableList,
+ preparer.GetContext(),
+ tableListPaths,
[&] (IRawBatchRequestPtr batch, const auto& table) {
- return batch->Get(preparer.GetTransactionId(), table.RichYPath->Path_ + "/@dynamic", TGetOptions());
+ // In case of external cluster, we can't use the current transaction
+ // since it is unknown for the external cluster.
+ // Hence, we should take a global transaction.
+ if (table.Cluster_ && !table.Cluster_->empty()) {
+ return batch->Get(TTransactionId(), table.Path_ + "/@dynamic", TGetOptions());
+ }
+ return batch->Get(preparer.GetTransactionId(), table.Path_ + "/@dynamic", TGetOptions());
});
auto newTableList = tableList;
diff --git a/yt/cpp/mapreduce/client/skiff.cpp b/yt/cpp/mapreduce/client/skiff.cpp
index 537a28f059..e9c5ba2680 100644
--- a/yt/cpp/mapreduce/client/skiff.cpp
+++ b/yt/cpp/mapreduce/client/skiff.cpp
@@ -279,6 +279,7 @@ TFormat CreateSkiffFormat(const NSkiff::TSkiffSchemaPtr& schema) {
NSkiff::TSkiffSchemaPtr CreateSkiffSchemaIfNecessary(
const IRawClientPtr& rawClient,
+ const TClientContext& context,
const TTransactionId& transactionId,
ENodeReaderFormat nodeReaderFormat,
const TVector<TRichYPath>& tablePaths,
@@ -301,17 +302,23 @@ NSkiff::TSkiffSchemaPtr CreateSkiffSchemaIfNecessary(
}
}
- auto nodes = NRawClient::BatchTransform(
+ auto nodes = RemoteClustersBatchTransform(
rawClient,
- NRawClient::CanonizeYPaths(rawClient, tablePaths),
+ context,
+ tablePaths,
[&] (IRawBatchRequestPtr batch, const TRichYPath& path) {
auto getOptions = TGetOptions()
.AttributeFilter(
TAttributeFilter()
.AddAttribute("schema")
.AddAttribute("dynamic")
- .AddAttribute("type")
- );
+ .AddAttribute("type"));
+ // In case of external cluster, we can't use the current transaction
+ // since it is unknown for the external cluster.
+ // Hence, we should take a global transaction.
+ if (path.Cluster_ && !path.Cluster_->empty()) {
+ return batch->Get(TTransactionId(), path.Path_, getOptions);
+ }
return batch->Get(transactionId, path.Path_, getOptions);
});
diff --git a/yt/cpp/mapreduce/client/skiff.h b/yt/cpp/mapreduce/client/skiff.h
index 5f26dc7656..3d9189998d 100644
--- a/yt/cpp/mapreduce/client/skiff.h
+++ b/yt/cpp/mapreduce/client/skiff.h
@@ -60,6 +60,7 @@ TFormat CreateSkiffFormat(const NSkiff::TSkiffSchemaPtr& schema);
NSkiff::TSkiffSchemaPtr CreateSkiffSchemaIfNecessary(
const IRawClientPtr& rawClient,
+ const TClientContext& context,
const TTransactionId& transactionId,
ENodeReaderFormat nodeReaderFormat,
const TVector<TRichYPath>& tablePaths,
diff --git a/yt/cpp/mapreduce/client/structured_table_formats.cpp b/yt/cpp/mapreduce/client/structured_table_formats.cpp
index 5df2c40873..ea511398cc 100644
--- a/yt/cpp/mapreduce/client/structured_table_formats.cpp
+++ b/yt/cpp/mapreduce/client/structured_table_formats.cpp
@@ -1,5 +1,6 @@
#include "structured_table_formats.h"
+#include "client.h"
#include "format_hints.h"
#include "skiff.h"
@@ -67,15 +68,23 @@ TMaybe<TNode> GetCommonTableFormat(
TMaybe<TNode> GetTableFormat(
const IClientRetryPolicyPtr& retryPolicy,
const IRawClientPtr& rawClient,
+ const TClientContext& context,
const TTransactionId& transactionId,
const TRichYPath& path)
{
+ auto newRawClient = rawClient;
+ if (path.Cluster_ && !path.Cluster_->empty()) {
+ auto newContext = context;
+ NDetail::SetupClusterContext(newContext, *path.Cluster_);
+ newRawClient = rawClient->Clone(newContext);
+ }
+
auto formatPath = path.Path_ + "/@_format";
auto exists = NDetail::RequestWithRetry<bool>(
retryPolicy->CreatePolicyForGenericRequest(),
- [&rawClient, &transactionId, &formatPath] (TMutationId /*mutationId*/) {
- return rawClient->Exists(transactionId, formatPath);
+ [&newRawClient, &transactionId, &formatPath] (TMutationId /*mutationId*/) {
+ return newRawClient->Exists(transactionId, formatPath);
});
if (!exists) {
return TMaybe<TNode>();
@@ -83,8 +92,8 @@ TMaybe<TNode> GetTableFormat(
auto format = NDetail::RequestWithRetry<TMaybe<TNode>>(
retryPolicy->CreatePolicyForGenericRequest(),
- [&rawClient, &transactionId, &formatPath] (TMutationId /*mutationId*/) {
- return rawClient->Get(transactionId, formatPath);
+ [&newRawClient, &transactionId, &formatPath] (TMutationId /*mutationId*/) {
+ return newRawClient->Get(transactionId, formatPath);
});
if (format.Get()->AsString() != "yamred_dsv") {
return TMaybe<TNode>();
@@ -102,12 +111,13 @@ TMaybe<TNode> GetTableFormat(
TMaybe<TNode> GetTableFormats(
const IClientRetryPolicyPtr& clientRetryPolicy,
const IRawClientPtr& rawClient,
+ const TClientContext& context,
const TTransactionId& transactionId,
const TVector<TRichYPath>& inputs)
{
TVector<TMaybe<TNode>> formats;
for (auto& table : inputs) {
- formats.push_back(GetTableFormat(clientRetryPolicy, rawClient, transactionId, table));
+ formats.push_back(GetTableFormat(clientRetryPolicy, rawClient, context, transactionId, table));
}
return GetCommonTableFormat(formats);
@@ -121,6 +131,7 @@ namespace NDetail {
NSkiff::TSkiffSchemaPtr TryCreateSkiffSchema(
const IRawClientPtr& rawClient,
+ const TClientContext& context,
const TTransactionId& transactionId,
const TVector<TRichYPath>& tables,
const TOperationOptions& options,
@@ -134,6 +145,7 @@ NSkiff::TSkiffSchemaPtr TryCreateSkiffSchema(
}
return CreateSkiffSchemaIfNecessary(
rawClient,
+ context,
transactionId,
nodeReaderFormat,
tables,
@@ -387,7 +399,7 @@ std::pair<TFormat, TMaybe<TSmallJobFile>> TFormatBuilder::CreateYamrFormat(
Y_ABORT_UNLESS(table.RichYPath, "Cannot use format from table for intermediate table");
tableList.push_back(*table.RichYPath);
}
- formatFromTableAttributes = GetTableFormats(ClientRetryPolicy_, RawClient_, TransactionId_, tableList);
+ formatFromTableAttributes = GetTableFormats(ClientRetryPolicy_, RawClient_, Context_, TransactionId_, tableList);
}
if (formatFromTableAttributes) {
return {
@@ -432,6 +444,7 @@ std::pair<TFormat, TMaybe<TSmallJobFile>> TFormatBuilder::CreateNodeFormat(
}
skiffSchema = TryCreateSkiffSchema(
RawClient_,
+ Context_,
TransactionId_,
tableList,
OperationOptions_,
diff --git a/yt/cpp/mapreduce/client/structured_table_formats.h b/yt/cpp/mapreduce/client/structured_table_formats.h
index 64a44c6f4d..c1ceec4066 100644
--- a/yt/cpp/mapreduce/client/structured_table_formats.h
+++ b/yt/cpp/mapreduce/client/structured_table_formats.h
@@ -22,12 +22,14 @@ TMaybe<TNode> GetCommonTableFormat(
TMaybe<TNode> GetTableFormat(
const IClientRetryPolicyPtr& clientRetryPolicy,
const IRawClientPtr& rawClient,
+ const TClientContext& context,
const TTransactionId& transactionId,
const TRichYPath& path);
TMaybe<TNode> GetTableFormats(
const IClientRetryPolicyPtr& clientRetryPolicy,
const IRawClientPtr& rawClient,
+ const TClientContext& context,
const TTransactionId& transactionId,
const TVector<TRichYPath>& paths);
diff --git a/yt/cpp/mapreduce/http/context.cpp b/yt/cpp/mapreduce/http/context.cpp
index 18d564fe09..531950c39e 100644
--- a/yt/cpp/mapreduce/http/context.cpp
+++ b/yt/cpp/mapreduce/http/context.cpp
@@ -13,7 +13,8 @@ bool operator==(const TClientContext& lhs, const TClientContext& rhs)
lhs.HttpClient == rhs.HttpClient &&
lhs.UseTLS == rhs.UseTLS &&
lhs.TvmOnly == rhs.TvmOnly &&
- lhs.ProxyAddress == rhs.ProxyAddress;
+ lhs.ProxyAddress == rhs.ProxyAddress &&
+ lhs.ProxyRole == rhs.ProxyRole;
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/yt/cpp/mapreduce/http/context.h b/yt/cpp/mapreduce/http/context.h
index f50c1b9732..4559cfd587 100644
--- a/yt/cpp/mapreduce/http/context.h
+++ b/yt/cpp/mapreduce/http/context.h
@@ -22,6 +22,7 @@ struct TClientContext
bool UseTLS = false;
TConfigPtr Config = TConfig::Get();
TMaybe<TString> ProxyAddress;
+ TMaybe<TString> ProxyRole;
};
bool operator==(const TClientContext& lhs, const TClientContext& rhs);
diff --git a/yt/cpp/mapreduce/http_client/raw_client.cpp b/yt/cpp/mapreduce/http_client/raw_client.cpp
index c86fd2494d..734282c2cf 100644
--- a/yt/cpp/mapreduce/http_client/raw_client.cpp
+++ b/yt/cpp/mapreduce/http_client/raw_client.cpp
@@ -903,6 +903,11 @@ IRawClientPtr THttpRawClient::Clone()
return ::MakeIntrusive<THttpRawClient>(Context_);
}
+IRawClientPtr THttpRawClient::Clone(const TClientContext& context)
+{
+ return ::MakeIntrusive<THttpRawClient>(context);
+}
+
////////////////////////////////////////////////////////////////////////////////
} // namespace NYT::NDetail
diff --git a/yt/cpp/mapreduce/http_client/raw_client.h b/yt/cpp/mapreduce/http_client/raw_client.h
index 6aa670f524..d292688978 100644
--- a/yt/cpp/mapreduce/http_client/raw_client.h
+++ b/yt/cpp/mapreduce/http_client/raw_client.h
@@ -339,6 +339,8 @@ public:
IRawClientPtr Clone() override;
+ IRawClientPtr Clone(const TClientContext& context) override;
+
private:
const TClientContext Context_;
};
diff --git a/yt/cpp/mapreduce/http_client/raw_requests.h b/yt/cpp/mapreduce/http_client/raw_requests.h
index 7273b8ad95..a61f303a62 100644
--- a/yt/cpp/mapreduce/http_client/raw_requests.h
+++ b/yt/cpp/mapreduce/http_client/raw_requests.h
@@ -4,6 +4,8 @@
#include <yt/cpp/mapreduce/common/fwd.h>
+#include <yt/cpp/mapreduce/client/client.h>
+
#include <yt/cpp/mapreduce/http/context.h>
#include <yt/cpp/mapreduce/interface/client.h>
@@ -91,6 +93,52 @@ auto BatchTransform(
return result;
}
+template <typename TBatchAdder>
+auto RemoteClustersBatchTransform(
+ const IRawClientPtr& rawClient,
+ const TClientContext& context,
+ const TVector<TRichYPath>& paths,
+ TBatchAdder batchAdder,
+ const TExecuteBatchOptions& options = {})
+{
+ // Given inputs from multiple clusters, we need to categorize them based on their cluster names.
+ // We assume the current cluster name is empty. Within each cluster,
+ // gather all related inputs to perform a batch request.
+ std::unordered_map<TString, std::vector<int>> clusterToPathsIndexes;
+ for (ssize_t index = 0; index < std::ssize(paths); ++index) {
+ const auto& path = paths[index];
+ auto clusterName = path.Cluster_.GetOrElse("");
+ clusterToPathsIndexes[clusterName].push_back(index);
+ }
+
+ std::vector<TNode> result(paths.size());
+ for (const auto& [clusterName, pathsIndexes] : clusterToPathsIndexes) {
+ auto newContext = context;
+ if (!clusterName.empty()) {
+ // It is not a current cluster, we switch the cluster context.
+ SetupClusterContext(newContext, clusterName);
+ }
+ auto newRawClient = rawClient->Clone(newContext);
+
+ TVector<TRichYPath> pathsBatch;
+ pathsBatch.reserve(pathsIndexes.size());
+ for (int index : pathsIndexes) {
+ pathsBatch.push_back(paths[index]);
+ }
+
+ auto batchResult = NRawClient::BatchTransform(
+ newRawClient,
+ NRawClient::CanonizeYPaths(newRawClient, pathsBatch),
+ batchAdder,
+ options);
+
+ for (ssize_t index = 0; index < std::ssize(pathsIndexes); ++index) {
+ result[pathsIndexes[index]] = batchResult[index];
+ }
+ }
+ return result;
+}
+
////////////////////////////////////////////////////////////////////////////////
} // namespace NDetail::NRawClient
diff --git a/yt/cpp/mapreduce/interface/common.cpp b/yt/cpp/mapreduce/interface/common.cpp
index 966be8341f..7abbef9127 100644
--- a/yt/cpp/mapreduce/interface/common.cpp
+++ b/yt/cpp/mapreduce/interface/common.cpp
@@ -552,6 +552,28 @@ TKeyBound::TKeyBound(ERelation relation, TKey key)
, Key_(std::move(key))
{ }
+bool operator==(const TKeyBound& lhs, const TKeyBound& rhs) noexcept
+{
+ return lhs.Key() == rhs.Key() && lhs.Relation() == rhs.Relation();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool operator==(const TReadLimit& lhs, const TReadLimit& rhs) noexcept
+{
+ return lhs.Key_ == rhs.Key_ && lhs.RowIndex_ == rhs.RowIndex_ &&
+ lhs.Offset_ == rhs.Offset_ && lhs.TabletIndex_ == rhs.TabletIndex_ &&
+ lhs.KeyBound_ == rhs.KeyBound_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool operator==(const TReadRange& lhs, const TReadRange& rhs) noexcept
+{
+ return lhs.LowerLimit_ == rhs.LowerLimit_ &&
+ lhs.UpperLimit_ == rhs.UpperLimit_ && lhs.Exact_ == rhs.Exact_;
+}
+
////////////////////////////////////////////////////////////////////////////////
TTableSchema CreateTableSchema(
diff --git a/yt/cpp/mapreduce/interface/common.h b/yt/cpp/mapreduce/interface/common.h
index d595302bbb..9752e15822 100644
--- a/yt/cpp/mapreduce/interface/common.h
+++ b/yt/cpp/mapreduce/interface/common.h
@@ -880,6 +880,9 @@ struct TKeyBound
/// @endcond
};
+/// Equality check checks all fields of TKeyBound
+bool operator==(const TKeyBound& lhs, const TKeyBound& rhs) noexcept;
+
///
/// @brief Description of the read limit.
///
@@ -923,6 +926,9 @@ struct TReadLimit
FLUENT_FIELD_OPTION(i64, TabletIndex);
};
+/// Equality check checks all fields of TReadLimit
+bool operator==(const TReadLimit& lhs, const TReadLimit& rhs) noexcept;
+
///
/// @brief Range of a table or a file
///
@@ -963,6 +969,9 @@ struct TReadRange
}
};
+/// Equality check checks all fields of TReadRange
+bool operator==(const TReadRange& lhs, const TReadRange& rhs) noexcept;
+
///
/// @brief Path with additional attributes.
///
diff --git a/yt/cpp/mapreduce/interface/raw_client.h b/yt/cpp/mapreduce/interface/raw_client.h
index b97c06ce37..24f8de61b6 100644
--- a/yt/cpp/mapreduce/interface/raw_client.h
+++ b/yt/cpp/mapreduce/interface/raw_client.h
@@ -4,6 +4,8 @@
#include "client_method_options.h"
#include "operation.h"
+#include <yt/cpp/mapreduce/http/context.h>
+
namespace NYT {
////////////////////////////////////////////////////////////////////////////////
@@ -198,6 +200,7 @@ public:
const TGetJobTraceOptions& options = {}) = 0;
// Files
+
virtual std::unique_ptr<IInputStream> ReadFile(
const TTransactionId& transactionId,
const TRichYPath& path,
@@ -332,7 +335,11 @@ public:
virtual IRawBatchRequestPtr CreateRawBatchRequest() = 0;
+ // Other
+
virtual IRawClientPtr Clone() = 0;
+
+ virtual IRawClientPtr Clone(const TClientContext& context) = 0;
};
////////////////////////////////////////////////////////////////////////////////
diff --git a/yt/python/yt/common.py b/yt/python/yt/common.py
index 975c6be6b0..26a9599e51 100644
--- a/yt/python/yt/common.py
+++ b/yt/python/yt/common.py
@@ -223,6 +223,10 @@ class YtError(Exception):
"""Already exists."""
return self.contains_code(501)
+ def is_authentication_error(self):
+ """Authentication error."""
+ return self.contains_code(900)
+
def is_access_denied(self):
"""Access denied."""
return self.contains_code(901)
diff --git a/yt/yql/providers/yt/codec/codegen/llvm14/ya.make b/yt/yql/providers/yt/codec/codegen/llvm14/ya.make
deleted file mode 100644
index 19bd72186e..0000000000
--- a/yt/yql/providers/yt/codec/codegen/llvm14/ya.make
+++ /dev/null
@@ -1,14 +0,0 @@
-LIBRARY()
-
-SRCDIR(yt/yql/providers/yt/codec/codegen/llvm14)
-
-PEERDIR(
- yql/essentials/minikql/codegen/llvm14
-)
-
-USE_LLVM_BC14()
-SET(LLVM_VER 14)
-
-INCLUDE(../ya.make.inc)
-
-END()
diff --git a/yt/yql/providers/yt/codec/codegen/ut/llvm14/ya.make b/yt/yql/providers/yt/codec/codegen/ut/llvm14/ya.make
deleted file mode 100644
index 64a0664f8b..0000000000
--- a/yt/yql/providers/yt/codec/codegen/ut/llvm14/ya.make
+++ /dev/null
@@ -1,12 +0,0 @@
-UNITTEST()
-
-SRCDIR(yt/yql/providers/yt/codec/codegen/ut/llvm14)
-
-PEERDIR(
- yql/essentials/minikql/computation/llvm14
- yt/yql/providers/yt/codec/codegen/llvm14
-)
-
-INCLUDE(../ya.make.inc)
-
-END()
diff --git a/yt/yql/providers/yt/codec/codegen/ut/ya.make b/yt/yql/providers/yt/codec/codegen/ut/ya.make
index 48a2e6f160..35ebc6804c 100644
--- a/yt/yql/providers/yt/codec/codegen/ut/ya.make
+++ b/yt/yql/providers/yt/codec/codegen/ut/ya.make
@@ -1 +1,3 @@
-RECURSE(llvm14 llvm16)
+RECURSE(
+ llvm16
+)
diff --git a/yt/yql/providers/yt/codec/codegen/ya.make b/yt/yql/providers/yt/codec/codegen/ya.make
index 4b5f83d5fe..51bc620d5e 100644
--- a/yt/yql/providers/yt/codec/codegen/ya.make
+++ b/yt/yql/providers/yt/codec/codegen/ya.make
@@ -6,6 +6,9 @@ PEERDIR()
END()
-RECURSE(llvm14 llvm16 no_llvm)
+RECURSE(
+ llvm16
+ no_llvm
+)
RECURSE_FOR_TESTS(ut)
diff --git a/yt/yql/providers/yt/comp_nodes/dq/llvm14/ya.make b/yt/yql/providers/yt/comp_nodes/dq/llvm14/ya.make
deleted file mode 100644
index 8d2e1c8ff9..0000000000
--- a/yt/yql/providers/yt/comp_nodes/dq/llvm14/ya.make
+++ /dev/null
@@ -1,11 +0,0 @@
-LIBRARY()
-
-PEERDIR(
- yt/yql/providers/yt/comp_nodes/dq
- yql/essentials/minikql/computation/llvm14
-)
-
-INCLUDE(../ya.make.inc)
-
-END()
-
diff --git a/yt/yql/providers/yt/comp_nodes/dq/ya.make b/yt/yql/providers/yt/comp_nodes/dq/ya.make
index 4b86e9e9b2..baf5fc72d9 100644
--- a/yt/yql/providers/yt/comp_nodes/dq/ya.make
+++ b/yt/yql/providers/yt/comp_nodes/dq/ya.make
@@ -7,4 +7,6 @@ PEERDIR(
END()
-RECURSE(llvm14 llvm16)
+RECURSE(
+ llvm16
+)
diff --git a/yt/yql/providers/yt/comp_nodes/llvm14/ya.make b/yt/yql/providers/yt/comp_nodes/llvm14/ya.make
deleted file mode 100644
index e3e64bfe16..0000000000
--- a/yt/yql/providers/yt/comp_nodes/llvm14/ya.make
+++ /dev/null
@@ -1,13 +0,0 @@
-LIBRARY()
-
-NO_COMPILER_WARNINGS()
-
-PEERDIR(
- yql/essentials/minikql/codegen/llvm14
- yql/essentials/minikql/invoke_builtins/llvm14
- yt/yql/providers/yt/codec/codegen/llvm14
-)
-
-INCLUDE(../ya.make.inc)
-
-END()
diff --git a/yt/yql/providers/yt/comp_nodes/ut/llvm14/ya.make b/yt/yql/providers/yt/comp_nodes/ut/llvm14/ya.make
deleted file mode 100644
index 0dc094f653..0000000000
--- a/yt/yql/providers/yt/comp_nodes/ut/llvm14/ya.make
+++ /dev/null
@@ -1,11 +0,0 @@
-UNITTEST()
-
-PEERDIR(
- yt/yql/providers/yt/comp_nodes/llvm14
- yql/essentials/minikql/comp_nodes/llvm14
- yt/yql/providers/yt/comp_nodes/llvm14
-)
-
-INCLUDE(../ya.make.inc)
-
-END()
diff --git a/yt/yql/providers/yt/comp_nodes/ut/ya.make b/yt/yql/providers/yt/comp_nodes/ut/ya.make
index 48a2e6f160..35ebc6804c 100644
--- a/yt/yql/providers/yt/comp_nodes/ut/ya.make
+++ b/yt/yql/providers/yt/comp_nodes/ut/ya.make
@@ -1 +1,3 @@
-RECURSE(llvm14 llvm16)
+RECURSE(
+ llvm16
+)
diff --git a/yt/yql/providers/yt/comp_nodes/ya.make b/yt/yql/providers/yt/comp_nodes/ya.make
index 01bbc05e6a..8e6ca98bd0 100644
--- a/yt/yql/providers/yt/comp_nodes/ya.make
+++ b/yt/yql/providers/yt/comp_nodes/ya.make
@@ -3,7 +3,6 @@ LIBRARY()
END()
RECURSE(
- llvm14
llvm16
no_llvm
)
diff --git a/yt/yql/providers/yt/gateway/file/yql_yt_file.cpp b/yt/yql/providers/yt/gateway/file/yql_yt_file.cpp
index 3a1e127f3d..f66e356c60 100644
--- a/yt/yql/providers/yt/gateway/file/yql_yt_file.cpp
+++ b/yt/yql/providers/yt/gateway/file/yql_yt_file.cpp
@@ -924,8 +924,15 @@ public:
if (interval || isDuration) {
attrs["expiration_timeout"] = isDuration ? duration.MilliSeconds() : interval->MilliSeconds();
}
- if (options.Config()->NightlyCompress.Get(cluster).GetOrElse(false)) {
- attrs["force_nightly_compress"] = true;
+ const TMaybe<bool> nightlyCompress = options.Config()->NightlyCompress.Get(cluster);
+ if (nightlyCompress.Defined()) {
+ if (*nightlyCompress) {
+ attrs["force_nightly_compress"] = true;
+ } else {
+ NYT::TNode compressSettings = NYT::TNode::CreateMap();
+ compressSettings["enabled"] = false;
+ attrs["nightly_compression_settings"] = compressSettings;
+ }
}
}
diff --git a/yt/yql/providers/yt/gateway/native/yql_yt_native.cpp b/yt/yql/providers/yt/gateway/native/yql_yt_native.cpp
index fb021d149c..a0ab23f0bf 100644
--- a/yt/yql/providers/yt/gateway/native/yql_yt_native.cpp
+++ b/yt/yql/providers/yt/gateway/native/yql_yt_native.cpp
@@ -67,6 +67,7 @@
#include <util/system/execpath.h>
#include <util/system/guard.h>
#include <util/system/shellcommand.h>
+#include <util/system/mutex.h>
#include <util/ysaveload.h>
#include <algorithm>
@@ -2533,8 +2534,16 @@ private:
yqlAttrs["expiration_timeout"] = isDuration ? duration.MilliSeconds()
: (*interval).MilliSeconds();
}
- if (execCtx->Options_.Config()->NightlyCompress.Get(cluster).GetOrElse(false)) {
- yqlAttrs["force_nightly_compress"] = true;
+ const TMaybe<bool> nightlyCompress =
+ execCtx->Options_.Config()->NightlyCompress.Get(cluster);
+ if (nightlyCompress.Defined()) {
+ if (*nightlyCompress) {
+ yqlAttrs["force_nightly_compress"] = true;
+ } else {
+ NYT::TNode compressSettings = NYT::TNode::CreateMap();
+ compressSettings["enabled"] = false;
+ yqlAttrs["nightly_compression_settings"] = compressSettings;
+ }
}
}
@@ -2803,76 +2812,73 @@ private:
TTableInfoResult& result)
{
TVector<NYT::TNode> attributes(tables.size());
- TVector<TMaybe<NYT::TNode>> linkAttributes(tables.size());
+ NSorted::TSimpleMap<size_t, TString> requestSchemasIdxs;
{
+ TMutex lock;
auto batchGet = tx->CreateBatchRequest();
TVector<TFuture<void>> batchRes(Reserve(idxs.size()));
for (auto& idx: idxs) {
- batchRes.push_back(batchGet->Get(tables[idx.first].Table() + "&/@").Apply(
- [&attributes, &linkAttributes, idx] (const TFuture<NYT::TNode>& res) {
- try {
- NYT::TNode attrs = res.GetValue();
- auto type = GetTypeFromAttributes(attrs, false);
- if (type == "link") {
- linkAttributes[idx.first] = attrs;
- } else {
- attributes[idx.first] = attrs;
+ batchRes.push_back(batchGet->Get(idx.second + "/@").Apply([&attributes, &requestSchemasIdxs, &lock, idx] (const TFuture<NYT::TNode>& res) {
+ attributes[idx.first] = res.GetValue();
+ if (attributes[idx.first].HasKey("schema") && attributes[idx.first]["schema"].IsEntity()) {
+ with_lock (lock) {
+ requestSchemasIdxs.push_back(idx);
}
- } catch (const TErrorResponse& e) {
- // Yt returns NoSuchTransaction as inner issue for ResolveError
- if (!e.IsResolveError() || e.IsNoSuchTransaction()) {
- throw;
- }
- // Just ignore. Original table path may be deleted at this time
}
}));
}
batchGet->ExecuteBatch();
WaitExceptionOrAll(batchRes).GetValue();
}
-
+ if (!requestSchemasIdxs.empty()) {
+ YQL_CLOG(INFO, ProviderYt) << "Additional request of @schema for " << requestSchemasIdxs.size() << " table(s)";
+ auto batchGet = tx->CreateBatchRequest();
+ TVector<TFuture<void>> batchRes(Reserve(requestSchemasIdxs.size()));
+ auto getOpts = TGetOptions()
+ .AttributeFilter(TAttributeFilter()
+ .AddAttribute("schema")
+ );
+ for (auto& idx: requestSchemasIdxs) {
+ batchRes.push_back(batchGet->Get(idx.second + "/@", getOpts).Apply([&attributes, idx] (const TFuture<NYT::TNode>& res) {
+ attributes[idx.first]["schema"] = res.GetValue().At("schema");
+ }));
+ }
+ batchGet->ExecuteBatch();
+ WaitExceptionOrAll(batchRes).GetValue();
+ }
{
- auto schemaAttrFilter = TAttributeFilter().AddAttribute("schema");
- TVector<NYT::TNode> schemas(tables.size());
-
auto batchGet = tx->CreateBatchRequest();
TVector<TFuture<void>> batchRes;
- for (auto& idx : idxs) {
- batchRes.push_back(batchGet->Get(tables[idx.first].Table() + "/@", TGetOptions().AttributeFilter(schemaAttrFilter))
- .Apply([idx, &schemas] (const TFuture<NYT::TNode>& res) {
- try {
- schemas[idx.first] = res.GetValue();
- } catch (const TErrorResponse& e) {
- // Yt returns NoSuchTransaction as inner issue for ResolveError
- if (!e.IsResolveError() || e.IsNoSuchTransaction()) {
- throw;
- }
- // Just ignore. Original table path may be deleted at this time
- }
- }));
-
- if (linkAttributes[idx.first]) {
- const auto& linkAttr = *linkAttributes[idx.first];
- batchRes.push_back(batchGet->Get(idx.second + "/@").Apply(
- [idx, &linkAttr, &attributes](const TFuture<NYT::TNode>& f) {
+ auto getOpts = TGetOptions()
+ .AttributeFilter(TAttributeFilter()
+ .AddAttribute("type")
+ .AddAttribute(TString{QB2Premapper})
+ .AddAttribute(TString{YqlRowSpecAttribute})
+ );
+ for (auto& idx: idxs) {
+ batchRes.push_back(batchGet->Get(tables[idx.first].Table() + "&/@", getOpts).Apply([idx, &attributes](const TFuture<NYT::TNode>& f) {
+ try {
NYT::TNode attrs = f.GetValue();
- attributes[idx.first] = attrs;
- // override some attributes by the link ones
- if (linkAttr.HasKey(QB2Premapper)) {
- attributes[idx.first][QB2Premapper] = linkAttr[QB2Premapper];
+ if (GetTypeFromAttributes(attrs, false) == "link") {
+ // override some attributes by the link ones
+ if (attrs.HasKey(QB2Premapper)) {
+ attributes[idx.first][QB2Premapper] = attrs[QB2Premapper];
+ }
+ if (attrs.HasKey(YqlRowSpecAttribute)) {
+ attributes[idx.first][YqlRowSpecAttribute] = attrs[YqlRowSpecAttribute];
+ }
}
- if (linkAttr.HasKey(YqlRowSpecAttribute)) {
- attributes[idx.first][YqlRowSpecAttribute] = linkAttr[YqlRowSpecAttribute];
+ } catch (const TErrorResponse& e) {
+ // Yt returns NoSuchTransaction as inner issue for ResolveError
+ if (!e.IsResolveError() || e.IsNoSuchTransaction()) {
+ throw;
}
- }));
- }
+ // Just ignore. Original table path may be deleted at this time
+ }
+ }));
}
batchGet->ExecuteBatch();
WaitExceptionOrAll(batchRes).GetValue();
-
- for (auto& idx: idxs) {
- attributes[idx.first]["schema"] = schemas[idx.first]["schema"];
- }
}
auto batchGet = tx->CreateBatchRequest();
@@ -2904,6 +2910,7 @@ private:
}
statInfo->Id = attrs["id"].AsString();
+ YQL_ENSURE(statInfo->Id == TStringBuf(idx.second).Skip(1));
statInfo->TableRevision = attrs["revision"].IntCast<ui64>();
statInfo->Revision = GetContentRevision(attrs);
diff --git a/yt/yql/providers/yt/provider/phy_opt/yql_yt_phy_opt_sort.cpp b/yt/yql/providers/yt/provider/phy_opt/yql_yt_phy_opt_sort.cpp
index 9300a67d13..c76709f914 100644
--- a/yt/yql/providers/yt/provider/phy_opt/yql_yt_phy_opt_sort.cpp
+++ b/yt/yql/providers/yt/provider/phy_opt/yql_yt_phy_opt_sort.cpp
@@ -667,11 +667,60 @@ TMaybeNode<TExprBase> TYtPhysicalOptProposalTransformer::AssumeConstraints(TExpr
const size_t index = FromString(input.Cast<TYtOutput>().OutIndex().Value());
TYtOutTableInfo outTable(op.Output().Item(index));
if (builder) {
+ YQL_ENSURE(!builder->NeedMap());
builder->FillRowSpecSort(*outTable.RowSpec);
}
outTable.RowSpec->SetConstraints(assume.Ref().GetConstraintSet());
outTable.SetUnique(assume.Ref().GetConstraint<TDistinctConstraintNode>(), assume.Pos(), ctx);
+ if (op.Maybe<TYtMap>() || op.Maybe<TYtReduce>()) {
+ TExprNode::TPtr lambda;
+ size_t childToReplace = 0;
+ if (auto map = op.Maybe<TYtMap>()) {
+ lambda = map.Cast().Mapper().Ptr();
+ childToReplace = TYtMap::idx_Mapper;
+ } else if (auto reduce = op.Maybe<TYtReduce>()) {
+ lambda = reduce.Cast().Reducer().Ptr();
+ childToReplace = TYtReduce::idx_Reducer;
+ } else {
+ YQL_ENSURE(false, "unexpected operation");
+ }
+
+ auto actualLambdaOutputType = TCoLambda(lambda).Body().Ref().GetTypeAnn();
+ auto expectedLambdaOutputType = outTable.RowSpec->GetExtendedType(ctx);
+ if (!IsSameAnnotation(*actualLambdaOutputType, *expectedLambdaOutputType)) {
+ // Drop aux columns that are not expected after row spec rebuild
+
+ auto excludeLambda = Build<TCoLambda>(ctx, lambda->Pos())
+ .Args({"stream"})
+ .Body<TCoOrderedMap>()
+ .Input("stream")
+ .Lambda<TCoLambda>()
+ .Args({"struct"})
+ .Body<TCoCastStruct>()
+ .Struct("struct")
+ .Type(ExpandType(lambda->Pos(), *expectedLambdaOutputType, ctx))
+ .Build()
+ .Build()
+ .Build()
+ .Done();
+
+ lambda = Build<TCoLambda>(ctx, lambda->Pos())
+ .Args({"stream"})
+ .Body<TExprApplier>()
+ .Apply(excludeLambda)
+ .With<TExprApplier>(0)
+ .Apply(TCoLambda(lambda))
+ .With(0, "stream")
+ .Build()
+ .Build()
+ .Done()
+ .Ptr();
+
+ newOp = ctx.ChangeChild(*newOp, childToReplace, std::move(lambda));
+ }
+ }
+
TVector<TYtOutTable> outputs;
for (size_t i = 0; i < op.Output().Size(); ++i) {
if (index == i) {
diff --git a/yt/yql/tests/sql/suites/order_by/input_sorted_desc.txt b/yt/yql/tests/sql/suites/order_by/input_sorted_desc.txt
new file mode 100644
index 0000000000..93fc12aa26
--- /dev/null
+++ b/yt/yql/tests/sql/suites/order_by/input_sorted_desc.txt
@@ -0,0 +1,3 @@
+{"_yql_column_0"="\xE0\xCC\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE";"key2"=3;"key1"=3;};
+{"_yql_column_0"="\xE0\xCD\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE";"key2"=2;"key1"=2;};
+{"_yql_column_0"="\xE0\xCE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE";"key2"=1;"key1"=1;};
diff --git a/yt/yql/tests/sql/suites/order_by/input_sorted_desc.txt.attr b/yt/yql/tests/sql/suites/order_by/input_sorted_desc.txt.attr
new file mode 100644
index 0000000000..21b8210d8e
--- /dev/null
+++ b/yt/yql/tests/sql/suites/order_by/input_sorted_desc.txt.attr
@@ -0,0 +1,61 @@
+{
+ "_yql_row_spec" = {
+ "Constraints" = {
+ "Sorted" = [
+ [
+ [
+ "key1";
+ ];
+ %false;
+ ];
+ [
+ [
+ "key2";
+ ];
+ %true;
+ ];
+ ];
+ };
+ "SortDirections" = [
+ 0;
+ 1;
+ ];
+ "SortMembers" = [
+ "key1";
+ "key2";
+ ];
+ "SortedBy" = [
+ "_yql_column_0";
+ "key2";
+ ];
+ "SortedByTypes" = [
+ [
+ "DataType";
+ "String";
+ ];
+ [
+ "DataType";
+ "Int32";
+ ];
+ ];
+ "Type" = [
+ "StructType";
+ [
+ [
+ "key1";
+ [
+ "DataType";
+ "Int32";
+ ];
+ ];
+ [
+ "key2";
+ [
+ "DataType";
+ "Int32";
+ ];
+ ];
+ ];
+ ];
+ };
+}
diff --git a/yt/yql/tests/sql/suites/order_by/yql-19598.cfg b/yt/yql/tests/sql/suites/order_by/yql-19598.cfg
new file mode 100644
index 0000000000..b7ad0584fe
--- /dev/null
+++ b/yt/yql/tests/sql/suites/order_by/yql-19598.cfg
@@ -0,0 +1 @@
+in Input input_sorted_desc.txt
diff --git a/yt/yql/tests/sql/suites/order_by/yql-19598.sql b/yt/yql/tests/sql/suites/order_by/yql-19598.sql
new file mode 100644
index 0000000000..cab0eded47
--- /dev/null
+++ b/yt/yql/tests/sql/suites/order_by/yql-19598.sql
@@ -0,0 +1,4 @@
+/* postgres can not */
+USE plato;
+
+SELECT * FROM Input WHERE key2 = 2 ASSUME ORDER BY key2;
diff --git a/yt/yt/client/api/public.h b/yt/yt/client/api/public.h
index d289f6c9b7..20cfb6a553 100644
--- a/yt/yt/client/api/public.h
+++ b/yt/yt/client/api/public.h
@@ -84,7 +84,7 @@ DEFINE_ENUM(ETransactionCoordinatorPrepareMode,
((Late) (1))
);
-DEFINE_ENUM(EProxyType,
+DEFINE_ENUM(EProxyKind,
((Http) (1))
((Rpc) (2))
((Grpc) (3))
diff --git a/yt/yt/client/api/queue_transaction.h b/yt/yt/client/api/queue_transaction.h
index 1487cb0ca3..42cd2166de 100644
--- a/yt/yt/client/api/queue_transaction.h
+++ b/yt/yt/client/api/queue_transaction.h
@@ -111,4 +111,3 @@ struct IQueueTransaction
////////////////////////////////////////////////////////////////////////////////
} // namespace NYT::NApi
-
diff --git a/yt/yt/client/api/rpc_proxy/client_base.cpp b/yt/yt/client/api/rpc_proxy/client_base.cpp
index 5fd70dd7c3..89aa41b97a 100644
--- a/yt/yt/client/api/rpc_proxy/client_base.cpp
+++ b/yt/yt/client/api/rpc_proxy/client_base.cpp
@@ -137,9 +137,7 @@ TFuture<ITransactionPtr> TClientBase::StartTransaction(
req->set_type(static_cast<NProto::ETransactionType>(type));
req->set_timeout(ToProto(timeout));
- if (options.Deadline) {
- req->set_deadline(ToProto(*options.Deadline));
- }
+ YT_OPTIONAL_SET_PROTO(req, deadline, options.Deadline);
if (options.Id) {
ToProto(req->mutable_id(), options.Id);
}
@@ -251,9 +249,7 @@ TFuture<TYsonString> TClientBase::GetNode(
req->mutable_legacy_attributes()->set_all(true);
}
- if (options.MaxSize) {
- req->set_max_size(*options.MaxSize);
- }
+ YT_OPTIONAL_SET_PROTO(req, max_size, options.MaxSize);
ToProto(req->mutable_complexity_limits(), options.ComplexityLimits);
@@ -289,9 +285,7 @@ TFuture<TYsonString> TClientBase::ListNode(
req->mutable_legacy_attributes()->set_all(true);
}
- if (options.MaxSize) {
- req->set_max_size(*options.MaxSize);
- }
+ YT_OPTIONAL_SET_PROTO(req, max_size, options.MaxSize);
ToProto(req->mutable_complexity_limits(), options.ComplexityLimits);
@@ -423,12 +417,8 @@ TFuture<TLockNodeResult> TClientBase::LockNode(
req->set_mode(ToProto(mode));
req->set_waitable(options.Waitable);
- if (options.ChildKey) {
- req->set_child_key(*options.ChildKey);
- }
- if (options.AttributeKey) {
- req->set_attribute_key(*options.AttributeKey);
- }
+ YT_OPTIONAL_TO_PROTO(req, child_key, options.ChildKey);
+ YT_OPTIONAL_TO_PROTO(req, attribute_key, options.AttributeKey);
ToProto(req->mutable_transactional_options(), options);
ToProto(req->mutable_prerequisite_options(), options);
@@ -642,12 +632,8 @@ TFuture<IFileReaderPtr> TClientBase::CreateFileReader(
InitStreamingRequest(*req);
req->set_path(path);
- if (options.Offset) {
- req->set_offset(*options.Offset);
- }
- if (options.Length) {
- req->set_length(*options.Length);
- }
+ YT_OPTIONAL_SET_PROTO(req, offset, options.Offset);
+ YT_OPTIONAL_SET_PROTO(req, length, options.Length);
if (options.Config) {
req->set_config(ConvertToYsonString(*options.Config).ToString());
}
@@ -691,12 +677,8 @@ IJournalReaderPtr TClientBase::CreateJournalReader(
req->set_path(path);
- if (options.FirstRowIndex) {
- req->set_first_row_index(*options.FirstRowIndex);
- }
- if (options.RowCount) {
- req->set_row_count(*options.RowCount);
- }
+ YT_OPTIONAL_SET_PROTO(req, first_row_index, options.FirstRowIndex);
+ YT_OPTIONAL_SET_PROTO(req, row_count, options.RowCount);
if (options.Config) {
req->set_config(ConvertToYsonString(*options.Config).ToString());
}
@@ -868,9 +850,7 @@ TFuture<TUnversionedLookupRowsResult> TClientBase::LookupRows(
req->set_keep_missing_rows(options.KeepMissingRows);
req->set_enable_partial_result(options.EnablePartialResult);
req->set_replica_consistency(static_cast<NProto::EReplicaConsistency>(options.ReplicaConsistency));
- if (options.UseLookupCache) {
- req->set_use_lookup_cache(*options.UseLookupCache);
- }
+ YT_OPTIONAL_SET_PROTO(req, use_lookup_cache, options.UseLookupCache);
req->SetMultiplexingBand(options.MultiplexingBand);
req->set_multiplexing_band(static_cast<NProto::EMultiplexingBand>(options.MultiplexingBand));
@@ -920,9 +900,7 @@ TFuture<TVersionedLookupRowsResult> TClientBase::VersionedLookupRows(
req->set_keep_missing_rows(options.KeepMissingRows);
req->set_enable_partial_result(options.EnablePartialResult);
req->set_replica_consistency(static_cast<NProto::EReplicaConsistency>(options.ReplicaConsistency));
- if (options.UseLookupCache) {
- req->set_use_lookup_cache(*options.UseLookupCache);
- }
+ YT_OPTIONAL_SET_PROTO(req, use_lookup_cache, options.UseLookupCache);
req->SetMultiplexingBand(options.MultiplexingBand);
req->set_multiplexing_band(static_cast<NProto::EMultiplexingBand>(options.MultiplexingBand));
@@ -969,9 +947,7 @@ TFuture<std::vector<TUnversionedLookupRowsResult>> TClientBase::MultiLookupRows(
}
protoSubrequest->set_keep_missing_rows(subrequestOptions.KeepMissingRows);
protoSubrequest->set_enable_partial_result(subrequestOptions.EnablePartialResult);
- if (subrequestOptions.UseLookupCache) {
- protoSubrequest->set_use_lookup_cache(*subrequestOptions.UseLookupCache);
- }
+ YT_OPTIONAL_SET_PROTO(protoSubrequest, use_lookup_cache, subrequestOptions.UseLookupCache);
auto rowset = SerializeRowset(
subrequest.NameTable,
@@ -1079,30 +1055,22 @@ TFuture<TSelectRowsResult> TClientBase::SelectRows(
// TODO(lukyan): Move to FillRequestBySelectRowsOptionsBase
req->SetTimeout(options.Timeout.value_or(config->DefaultSelectRowsTimeout));
- if (options.InputRowLimit) {
- req->set_input_row_limit(*options.InputRowLimit);
- }
- if (options.OutputRowLimit) {
- req->set_output_row_limit(*options.OutputRowLimit);
- }
+ YT_OPTIONAL_SET_PROTO(req, input_row_limit, options.InputRowLimit);
+ YT_OPTIONAL_SET_PROTO(req, output_row_limit, options.OutputRowLimit);
req->set_range_expansion_limit(options.RangeExpansionLimit);
req->set_max_subqueries(options.MaxSubqueries);
req->set_min_row_count_per_subquery(options.MinRowCountPerSubquery);
req->set_allow_full_scan(options.AllowFullScan);
req->set_allow_join_without_index(options.AllowJoinWithoutIndex);
- if (options.ExecutionPool) {
- req->set_execution_pool(*options.ExecutionPool);
- }
+ YT_OPTIONAL_TO_PROTO(req, execution_pool, options.ExecutionPool);
if (options.PlaceholderValues) {
req->set_placeholder_values(options.PlaceholderValues.ToString());
}
req->set_fail_on_incomplete_result(options.FailOnIncompleteResult);
req->set_verbose_logging(options.VerboseLogging);
req->set_new_range_inference(options.NewRangeInference);
- if (options.ExecutionBackend) {
- req->set_execution_backend(ToProto(*options.ExecutionBackend));
- }
+ YT_OPTIONAL_SET_PROTO(req, execution_backend, options.ExecutionBackend);
req->set_enable_code_cache(options.EnableCodeCache);
req->set_memory_limit_per_node(options.MemoryLimitPerNode);
ToProto(req->mutable_suppressable_access_tracking_options(), options);
@@ -1110,9 +1078,7 @@ TFuture<TSelectRowsResult> TClientBase::SelectRows(
req->set_use_canonical_null_relations(options.UseCanonicalNullRelations);
req->set_merge_versioned_rows(options.MergeVersionedRows);
ToProto(req->mutable_versioned_read_options(), options.VersionedReadOptions);
- if (options.UseLookupCache) {
- req->set_use_lookup_cache(*options.UseLookupCache);
- }
+ YT_OPTIONAL_SET_PROTO(req, use_lookup_cache, options.UseLookupCache);
return req->Invoke().Apply(BIND([] (const TApiServiceProxy::TRspSelectRowsPtr& rsp) {
TSelectRowsResult result;
diff --git a/yt/yt/client/api/rpc_proxy/client_impl.cpp b/yt/yt/client/api/rpc_proxy/client_impl.cpp
index 0de3dfac4e..33bad4dcfc 100644
--- a/yt/yt/client/api/rpc_proxy/client_impl.cpp
+++ b/yt/yt/client/api/rpc_proxy/client_impl.cpp
@@ -180,9 +180,7 @@ ITransactionPtr TClient::AttachTransaction(
ToProto(req->mutable_transaction_id(), transactionId);
// COMPAT(kiselyovp): remove auto_abort from the protocol
req->set_auto_abort(false);
- if (options.PingPeriod) {
- req->set_ping_period(options.PingPeriod->GetValue());
- }
+ YT_OPTIONAL_SET_PROTO(req, ping_period, options.PingPeriod);
req->set_ping(options.Ping);
req->set_ping_ancestors(options.PingAncestors);
@@ -366,12 +364,8 @@ TFuture<void> TClient::ReshardTable(
req->set_path(path);
req->set_tablet_count(tabletCount);
- if (options.Uniform) {
- req->set_uniform(*options.Uniform);
- }
- if (options.EnableSlicing) {
- req->set_enable_slicing(*options.EnableSlicing);
- }
+ YT_OPTIONAL_SET_PROTO(req, uniform, options.Uniform);
+ YT_OPTIONAL_SET_PROTO(req, enable_slicing, options.EnableSlicing);
if (options.SlicingAccuracy) {
req->set_slicing_accuracy(*options.SlicingAccuracy);
}
@@ -438,9 +432,7 @@ TFuture<void> TClient::AlterTable(
if (options.SchemaId) {
ToProto(req->mutable_schema_id(), *options.SchemaId);
}
- if (options.Dynamic) {
- req->set_dynamic(*options.Dynamic);
- }
+ YT_OPTIONAL_SET_PROTO(req, dynamic, options.Dynamic);
if (options.UpstreamReplicaId) {
ToProto(req->mutable_upstream_replica_id(), *options.UpstreamReplicaId);
}
@@ -468,29 +460,20 @@ TFuture<void> TClient::AlterTableReplica(
ToProto(req->mutable_replica_id(), replicaId);
- if (options.Enabled) {
- req->set_enabled(*options.Enabled);
- }
+ YT_OPTIONAL_SET_PROTO(req, enabled, options.Enabled);
if (options.Mode) {
req->set_mode(static_cast<NProto::ETableReplicaMode>(*options.Mode));
}
- if (options.PreserveTimestamps) {
- req->set_preserve_timestamps(*options.PreserveTimestamps);
- }
+ YT_OPTIONAL_SET_PROTO(req, preserve_timestamps, options.PreserveTimestamps);
if (options.Atomicity) {
req->set_atomicity(static_cast<NProto::EAtomicity>(*options.Atomicity));
}
- if (options.EnableReplicatedTableTracker) {
- req->set_enable_replicated_table_tracker(*options.EnableReplicatedTableTracker);
- }
-
- if (options.ReplicaPath) {
- req->set_replica_path(*options.ReplicaPath);
- }
+ YT_OPTIONAL_SET_PROTO(req, enable_replicated_table_tracker, options.EnableReplicatedTableTracker);
+ YT_OPTIONAL_TO_PROTO(req, replica_path, options.ReplicaPath);
ToProto(req->mutable_mutating_options(), options);
@@ -566,9 +549,7 @@ TFuture<std::vector<TTableReplicaId>> TClient::GetInSyncReplicas(
req->set_timestamp(options.Timestamp);
}
- if (options.CachedSyncReplicasTimeout) {
- req->set_cached_sync_replicas_timeout(NYT::ToProto(*options.CachedSyncReplicasTimeout));
- }
+ YT_OPTIONAL_SET_PROTO(req, cached_sync_replicas_timeout, options.CachedSyncReplicasTimeout);
req->set_path(path);
req->Attachments() = SerializeRowset(nameTable, keys, req->mutable_rowset_descriptor());
@@ -592,9 +573,7 @@ TFuture<std::vector<TTableReplicaId>> TClient::GetInSyncReplicas(
req->set_timestamp(options.Timestamp);
}
- if (options.CachedSyncReplicasTimeout) {
- req->set_cached_sync_replicas_timeout(NYT::ToProto(*options.CachedSyncReplicasTimeout));
- }
+ YT_OPTIONAL_SET_PROTO(req, cached_sync_replicas_timeout, options.CachedSyncReplicasTimeout);
req->set_path(path);
req->RequireServerFeature(ERpcProxyFeature::GetInSyncWithoutKeys);
@@ -659,9 +638,7 @@ TFuture<TGetTabletErrorsResult> TClient::GetTabletErrors(
SetTimeoutOptions(*req, options);
req->set_path(path);
- if (options.Limit) {
- req->set_limit(*options.Limit);
- }
+ YT_OPTIONAL_SET_PROTO(req, limit, options.Limit);
return req->Invoke().Apply(BIND([] (const TErrorOr<TApiServiceProxy::TRspGetTabletErrorsPtr>& rspOrError) {
const auto& rsp = rspOrError.ValueOrThrow();
@@ -739,12 +716,8 @@ TFuture<void> TClient::AlterReplicationCard(
if (options.ReplicatedTableOptions) {
req->set_replicated_table_options(ConvertToYsonString(options.ReplicatedTableOptions).ToString());
}
- if (options.EnableReplicatedTableTracker) {
- req->set_enable_replicated_table_tracker(*options.EnableReplicatedTableTracker);
- }
- if (options.ReplicationCardCollocationId) {
- ToProto(req->mutable_replication_card_collocation_id(), *options.ReplicationCardCollocationId);
- }
+ YT_OPTIONAL_SET_PROTO(req, enable_replicated_table_tracker, options.EnableReplicatedTableTracker);
+ YT_OPTIONAL_TO_PROTO(req, replication_card_collocation_id, options.ReplicationCardCollocationId);
if (options.CollocationOptions) {
req->set_collocation_options(ConvertToYsonString(options.CollocationOptions).ToString());
}
@@ -837,9 +810,7 @@ TFuture<IQueueRowsetPtr> TClient::PullQueueConsumer(
ToProto(req->mutable_consumer_path(), consumerPath);
ToProto(req->mutable_queue_path(), queuePath);
- if (offset) {
- req->set_offset(*offset);
- }
+ YT_OPTIONAL_SET_PROTO(req, offset, offset);
req->set_partition_index(partitionIndex);
ToProto(req->mutable_row_batch_read_options(), rowBatchReadOptions);
@@ -900,12 +871,8 @@ TFuture<std::vector<TListQueueConsumerRegistrationsResult>> TClient::ListQueueCo
auto req = proxy.ListQueueConsumerRegistrations();
SetTimeoutOptions(*req, options);
- if (queuePath) {
- ToProto(req->mutable_queue_path(), *queuePath);
- }
- if (consumerPath) {
- ToProto(req->mutable_consumer_path(), *consumerPath);
- }
+ YT_OPTIONAL_TO_PROTO(req, queue_path, queuePath);
+ YT_OPTIONAL_TO_PROTO(req, consumer_path, consumerPath);
return req->Invoke().Apply(BIND([] (const TApiServiceProxy::TRspListQueueConsumerRegistrationsPtr& rsp) {
std::vector<TListQueueConsumerRegistrationsResult> result;
@@ -1030,9 +997,7 @@ TFuture<TCheckPermissionResponse> TClient::CheckPermission(
auto* protoColumns = req->mutable_columns();
ToProto(protoColumns->mutable_items(), *options.Columns);
}
- if (options.Vital) {
- req->set_vital(*options.Vital);
- }
+ YT_OPTIONAL_SET_PROTO(req, vital, options.Vital);
ToProto(req->mutable_master_read_options(), options);
ToProto(req->mutable_transactional_options(), options);
@@ -1059,9 +1024,7 @@ TFuture<TCheckPermissionByAclResult> TClient::CheckPermissionByAcl(
auto req = proxy.CheckPermissionByAcl();
SetTimeoutOptions(*req, options);
- if (user) {
- req->set_user(ToProto(*user));
- }
+ YT_OPTIONAL_SET_PROTO(req, user, user);
req->set_permission(ToProto(permission));
req->set_acl(ConvertToYsonString(acl).ToString());
req->set_ignore_missing_subjects(options.IgnoreMissingSubjects);
@@ -1148,9 +1111,7 @@ TFuture<void> TClient::AbortOperation(
NScheduler::ToProto(req, operationIdOrAlias);
- if (options.AbortMessage) {
- req->set_abort_message(*options.AbortMessage);
- }
+ YT_OPTIONAL_TO_PROTO(req, abort_message, options.AbortMessage);
return req->Invoke().As<void>();
}
@@ -1166,9 +1127,7 @@ TFuture<void> TClient::SuspendOperation(
NScheduler::ToProto(req, operationIdOrAlias);
req->set_abort_running_jobs(options.AbortRunningJobs);
- if (options.Reason) {
- req->set_reason(*options.Reason);
- }
+ YT_OPTIONAL_TO_PROTO(req, reason, options.Reason);
return req->Invoke().As<void>();
}
@@ -1349,12 +1308,8 @@ TFuture<TGetJobStderrResponse> TClient::GetJobStderr(
NScheduler::ToProto(req, operationIdOrAlias);
ToProto(req->mutable_job_id(), jobId);
- if (options.Limit) {
- req->set_limit(*options.Limit);
- }
- if (options.Offset) {
- req->set_offset(*options.Offset);
- }
+ YT_OPTIONAL_SET_PROTO(req, limit, options.Limit);
+ YT_OPTIONAL_SET_PROTO(req, offset, options.Offset);
return req->Invoke().Apply(BIND([req = req](const TApiServiceProxy::TRspGetJobStderrPtr& rsp) {
YT_VERIFY(rsp->Attachments().size() == 1);
@@ -1373,24 +1328,12 @@ TFuture<std::vector<TJobTraceEvent>> TClient::GetJobTrace(
SetTimeoutOptions(*req, options);
NScheduler::ToProto(req, operationIdOrAlias);
- if (options.JobId) {
- ToProto(req->mutable_job_id(), *options.JobId);
- }
- if (options.TraceId) {
- ToProto(req->mutable_trace_id(), *options.TraceId);
- }
- if (options.FromTime) {
- req->set_from_time(*options.FromTime);
- }
- if (options.ToTime) {
- req->set_to_time(*options.ToTime);
- }
- if (options.FromEventIndex) {
- req->set_from_event_index(*options.FromEventIndex);
- }
- if (options.ToEventIndex) {
- req->set_to_event_index(*options.ToEventIndex);
- }
+ YT_OPTIONAL_TO_PROTO(req, job_id, options.JobId);
+ YT_OPTIONAL_TO_PROTO(req, trace_id, options.TraceId);
+ YT_OPTIONAL_SET_PROTO(req, from_time, options.FromTime);
+ YT_OPTIONAL_SET_PROTO(req, to_time, options.ToTime);
+ YT_OPTIONAL_SET_PROTO(req, from_event_index, options.FromEventIndex);
+ YT_OPTIONAL_SET_PROTO(req, to_event_index, options.ToEventIndex);
return req->Invoke().Apply(BIND([] (const TApiServiceProxy::TRspGetJobTracePtr& rsp) {
return FromProto<std::vector<TJobTraceEvent>>(rsp->events());
@@ -1425,19 +1368,11 @@ TFuture<TListOperationsResult> TClient::ListOperations(
auto req = proxy.ListOperations();
SetTimeoutOptions(*req, options);
- if (options.FromTime) {
- req->set_from_time(NYT::ToProto(*options.FromTime));
- }
- if (options.ToTime) {
- req->set_to_time(NYT::ToProto(*options.ToTime));
- }
- if (options.CursorTime) {
- req->set_cursor_time(NYT::ToProto(*options.CursorTime));
- }
+ YT_OPTIONAL_SET_PROTO(req, from_time, options.FromTime);
+ YT_OPTIONAL_SET_PROTO(req, to_time, options.ToTime);
+ YT_OPTIONAL_SET_PROTO(req, cursor_time, options.CursorTime);
req->set_cursor_direction(static_cast<NProto::EOperationSortDirection>(options.CursorDirection));
- if (options.UserFilter) {
- req->set_user_filter(*options.UserFilter);
- }
+ YT_OPTIONAL_TO_PROTO(req, user_filter, options.UserFilter);
if (options.AccessFilter) {
req->set_access_filter(ConvertToYsonString(options.AccessFilter).ToString());
@@ -1449,15 +1384,9 @@ TFuture<TListOperationsResult> TClient::ListOperations(
if (options.TypeFilter) {
req->set_type_filter(NProto::ConvertOperationTypeToProto(*options.TypeFilter));
}
- if (options.SubstrFilter) {
- req->set_substr_filter(*options.SubstrFilter);
- }
- if (options.Pool) {
- req->set_pool(*options.Pool);
- }
- if (options.PoolTree) {
- req->set_pool_tree(*options.PoolTree);
- }
+ YT_OPTIONAL_TO_PROTO(req, substr_filter, options.SubstrFilter);
+ YT_OPTIONAL_TO_PROTO(req, pool, options.Pool);
+ YT_OPTIONAL_TO_PROTO(req, pool_tree, options.PoolTree);
if (options.WithFailedJobs) {
req->set_with_failed_jobs(*options.WithFailedJobs);
}
diff --git a/yt/yt/client/api/rpc_proxy/helpers.cpp b/yt/yt/client/api/rpc_proxy/helpers.cpp
index c8acdeb3f8..f445a9c17f 100644
--- a/yt/yt/client/api/rpc_proxy/helpers.cpp
+++ b/yt/yt/client/api/rpc_proxy/helpers.cpp
@@ -96,9 +96,7 @@ void ToProto(
proto->set_expire_after_successful_update_time(ToProto(options.ExpireAfterSuccessfulUpdateTime));
proto->set_expire_after_failed_update_time(ToProto(options.ExpireAfterFailedUpdateTime));
proto->set_success_staleness_bound(ToProto(options.SuccessStalenessBound));
- if (options.CacheStickyGroupSize) {
- proto->set_cache_sticky_group_size(*options.CacheStickyGroupSize);
- }
+ YT_OPTIONAL_SET_PROTO(proto, cache_sticky_group_size, options.CacheStickyGroupSize);
}
void ToProto(
@@ -122,12 +120,8 @@ void ToProto(
NProto::TTabletRangeOptions* proto,
const NApi::TTabletRangeOptions& options)
{
- if (options.FirstTabletIndex) {
- proto->set_first_tablet_index(*options.FirstTabletIndex);
- }
- if (options.LastTabletIndex) {
- proto->set_last_tablet_index(*options.LastTabletIndex);
- }
+ YT_OPTIONAL_SET_PROTO(proto, first_tablet_index, options.FirstTabletIndex);
+ YT_OPTIONAL_SET_PROTO(proto, last_tablet_index, options.LastTabletIndex);
}
void ToProto(
@@ -135,9 +129,7 @@ void ToProto(
const NApi::TTabletReadOptionsBase& options)
{
protoOptions->set_read_from(static_cast<NProto::ETabletReadKind>(options.ReadFrom));
- if (options.CachedSyncReplicasTimeout) {
- protoOptions->set_cached_sync_replicas_timeout(ToProto(*options.CachedSyncReplicasTimeout));
- }
+ YT_OPTIONAL_SET_PROTO(protoOptions, cached_sync_replicas_timeout, options.CachedSyncReplicasTimeout);
}
////////////////////////////////////////////////////////////////////////////////
@@ -207,14 +199,10 @@ void ToProto(
proto->set_action(static_cast<NProto::ESecurityAction>(result.Action));
ToProto(proto->mutable_object_id(), result.ObjectId);
- if (result.ObjectName) {
- proto->set_object_name(*result.ObjectName);
- }
+ YT_OPTIONAL_TO_PROTO(proto, object_name, result.ObjectName);
ToProto(proto->mutable_subject_id(), result.SubjectId);
- if (result.SubjectName) {
- proto->set_subject_name(*result.SubjectName);
- }
+ YT_OPTIONAL_TO_PROTO(proto, subject_name, result.SubjectName);
}
void FromProto(
@@ -224,18 +212,10 @@ void FromProto(
result->Action = static_cast<NSecurityClient::ESecurityAction>(proto.action());
FromProto(&result->ObjectId, proto.object_id());
- if (proto.has_object_name()) {
- result->ObjectName = proto.object_name();
- } else {
- result->ObjectName.reset();
- }
+ result->ObjectName = YT_OPTIONAL_FROM_PROTO(proto, object_name);
FromProto(&result->SubjectId, proto.subject_id());
- if (proto.has_subject_name()) {
- result->SubjectName = proto.subject_name();
- } else {
- result->SubjectName.reset();
- }
+ result->SubjectName = YT_OPTIONAL_FROM_PROTO(proto, subject_name);
}
void ToProto(
@@ -247,9 +227,7 @@ void ToProto(
proto->set_action(static_cast<NProto::ESecurityAction>(result.Action));
ToProto(proto->mutable_subject_id(), result.SubjectId);
- if (result.SubjectName) {
- proto->set_subject_name(*result.SubjectName);
- }
+ YT_OPTIONAL_TO_PROTO(proto, subject_name, result.SubjectName);
ToProto(proto->mutable_missing_subjects(), result.MissingSubjects);
}
@@ -261,11 +239,7 @@ void FromProto(
result->Action = static_cast<NSecurityClient::ESecurityAction>(proto.action());
FromProto(&result->SubjectId, proto.subject_id());
- if (proto.has_subject_name()) {
- result->SubjectName = proto.subject_name();
- } else {
- result->SubjectName.reset();
- }
+ result->SubjectName = YT_OPTIONAL_FROM_PROTO(proto, subject_name);
FromProto(&result->MissingSubjects, proto.missing_subjects());
}
@@ -317,9 +291,7 @@ void ToProto(
}
}
- if (result.FailedJobsCount) {
- proto->set_failed_jobs_count(*result.FailedJobsCount);
- }
+ YT_OPTIONAL_SET_PROTO(proto, failed_jobs_count, result.FailedJobsCount);
proto->set_incomplete(result.Incomplete);
}
@@ -400,15 +372,9 @@ void ToProto(
proto->Clear();
ToProto(proto->mutable_jobs(), result.Jobs);
- if (result.CypressJobCount) {
- proto->set_cypress_job_count(*result.CypressJobCount);
- }
- if (result.ControllerAgentJobCount) {
- proto->set_controller_agent_job_count(*result.ControllerAgentJobCount);
- }
- if (result.ArchiveJobCount) {
- proto->set_archive_job_count(*result.ArchiveJobCount);
- }
+ YT_OPTIONAL_SET_PROTO(proto, cypress_job_count, result.CypressJobCount);
+ YT_OPTIONAL_SET_PROTO(proto, controller_agent_job_count, result.ControllerAgentJobCount);
+ YT_OPTIONAL_SET_PROTO(proto, archive_job_count, result.ArchiveJobCount);
if (result.ContinuationToken) {
proto->set_continuation_token(*result.ContinuationToken);
}
@@ -423,26 +389,10 @@ void FromProto(
{
FromProto(&result->Jobs, proto.jobs());
- if (proto.has_cypress_job_count()) {
- result->CypressJobCount = proto.cypress_job_count();
- } else {
- result->CypressJobCount.reset();
- }
- if (proto.has_controller_agent_job_count()) {
- result->ControllerAgentJobCount = proto.controller_agent_job_count();
- } else {
- result->ControllerAgentJobCount.reset();
- }
- if (proto.has_archive_job_count()) {
- result->ArchiveJobCount = proto.archive_job_count();
- } else {
- result->ArchiveJobCount.reset();
- }
- if (proto.has_continuation_token()) {
- result->ContinuationToken = proto.continuation_token();
- } else {
- result->ContinuationToken.reset();
- }
+ result->CypressJobCount = YT_OPTIONAL_FROM_PROTO(proto, cypress_job_count);
+ result->ControllerAgentJobCount = YT_OPTIONAL_FROM_PROTO(proto, controller_agent_job_count);
+ result->ArchiveJobCount = YT_OPTIONAL_FROM_PROTO(proto, archive_job_count);
+ result->ContinuationToken = YT_OPTIONAL_FROM_PROTO(proto, continuation_token);
FromProto(&result->Statistics, proto.statistics());
FromProto(&result->Errors, proto.errors());
@@ -483,46 +433,18 @@ void ToProto(NProto::TColumnSchema* protoSchema, const NTableClient::TColumnSche
protoSchema->set_type(ToProto(GetPhysicalType(schema.CastToV1Type())));
auto typeV3Yson = ConvertToYsonString(TTypeV3LogicalTypeWrapper{schema.LogicalType()});
protoSchema->set_type_v3(typeV3Yson.ToString());
- if (schema.Lock()) {
- protoSchema->set_lock(ToProto(*schema.Lock()));
- } else {
- protoSchema->clear_lock();
- }
- if (schema.Expression()) {
- protoSchema->set_expression(*schema.Expression());
- } else {
- protoSchema->clear_expression();
- }
- if (schema.Materialized()) {
- protoSchema->set_materialized(*schema.Materialized());
- } else {
- protoSchema->clear_materialized();
- }
- if (schema.Aggregate()) {
- protoSchema->set_aggregate(ToProto(*schema.Aggregate()));
- } else {
- protoSchema->clear_aggregate();
- }
- if (schema.SortOrder()) {
- protoSchema->set_sort_order(ToProto(*schema.SortOrder()));
- } else {
- protoSchema->clear_sort_order();
- }
- if (schema.Group()) {
- protoSchema->set_group(ToProto(*schema.Group()));
- } else {
- protoSchema->clear_group();
- }
+ YT_OPTIONAL_TO_PROTO(protoSchema, lock, schema.Lock());
+ YT_OPTIONAL_TO_PROTO(protoSchema, expression, schema.Expression());
+ YT_OPTIONAL_SET_PROTO(protoSchema, materialized, schema.Materialized());
+ YT_OPTIONAL_TO_PROTO(protoSchema, aggregate, schema.Aggregate());
+ YT_OPTIONAL_SET_PROTO(protoSchema, sort_order, schema.SortOrder());
+ YT_OPTIONAL_TO_PROTO(protoSchema, group, schema.Group());
if (schema.Required()) {
protoSchema->set_required(schema.Required());
} else {
protoSchema->clear_required();
}
- if (schema.MaxInlineHunkSize()) {
- protoSchema->set_max_inline_hunk_size(*schema.MaxInlineHunkSize());
- } else {
- protoSchema->clear_max_inline_hunk_size();
- }
+ YT_OPTIONAL_SET_PROTO(protoSchema, max_inline_hunk_size, schema.MaxInlineHunkSize());
}
void FromProto(NTableClient::TColumnSchema* schema, const NProto::TColumnSchema& protoSchema)
@@ -571,13 +493,13 @@ void FromProto(NTableClient::TColumnSchema* schema, const NProto::TColumnSchema&
}
schema->SetLogicalType(std::move(columnType));
- schema->SetLock(YT_PROTO_OPTIONAL(protoSchema, lock));
- schema->SetExpression(YT_PROTO_OPTIONAL(protoSchema, expression));
- schema->SetMaterialized(YT_PROTO_OPTIONAL(protoSchema, materialized));
- schema->SetAggregate(YT_PROTO_OPTIONAL(protoSchema, aggregate));
+ schema->SetLock(YT_OPTIONAL_FROM_PROTO(protoSchema, lock));
+ schema->SetExpression(YT_OPTIONAL_FROM_PROTO(protoSchema, expression));
+ schema->SetMaterialized(YT_OPTIONAL_FROM_PROTO(protoSchema, materialized));
+ schema->SetAggregate(YT_OPTIONAL_FROM_PROTO(protoSchema, aggregate));
schema->SetSortOrder(YT_APPLY_PROTO_OPTIONAL(protoSchema, sort_order, FromProto<ESortOrder>));
- schema->SetGroup(YT_PROTO_OPTIONAL(protoSchema, group));
- schema->SetMaxInlineHunkSize(YT_PROTO_OPTIONAL(protoSchema, max_inline_hunk_size));
+ schema->SetGroup(YT_OPTIONAL_FROM_PROTO(protoSchema, group));
+ schema->SetMaxInlineHunkSize(YT_OPTIONAL_FROM_PROTO(protoSchema, max_inline_hunk_size));
}
void ToProto(NProto::TTableSchema* protoSchema, const NTableClient::TTableSchema& schema)
@@ -773,38 +695,13 @@ void ToProto(NProto::TOperation* protoOperation, const NApi::TOperation& operati
void FromProto(NApi::TOperation* operation, const NProto::TOperation& protoOperation)
{
- if (protoOperation.has_id()) {
- operation->Id = FromProto<NScheduler::TOperationId>(protoOperation.id());
- } else {
- operation->Id.reset();
- }
- if (protoOperation.has_type()) {
- operation->Type = ConvertOperationTypeFromProto(protoOperation.type());
- } else {
- operation->Type.reset();
- }
- if (protoOperation.has_state()) {
- operation->State = ConvertOperationStateFromProto(protoOperation.state());
- } else {
- operation->State.reset();
- }
+ operation->Id = YT_APPLY_PROTO_OPTIONAL(protoOperation, id, FromProto<NScheduler::TOperationId>);
+ operation->Type = YT_APPLY_PROTO_OPTIONAL(protoOperation, type, ConvertOperationTypeFromProto);
+ operation->State = YT_APPLY_PROTO_OPTIONAL(protoOperation, state, ConvertOperationStateFromProto);
- if (protoOperation.has_start_time()) {
- operation->StartTime = TInstant::FromValue(protoOperation.start_time());
- } else {
- operation->StartTime.reset();
- }
- if (protoOperation.has_finish_time()) {
- operation->FinishTime = TInstant::FromValue(protoOperation.finish_time());
- } else {
- operation->FinishTime.reset();
- }
-
- if (protoOperation.has_authenticated_user()) {
- operation->AuthenticatedUser = protoOperation.authenticated_user();
- } else {
- operation->AuthenticatedUser.reset();
- }
+ operation->StartTime = YT_OPTIONAL_FROM_PROTO(protoOperation, start_time, TInstant);
+ operation->FinishTime = YT_OPTIONAL_FROM_PROTO(protoOperation, finish_time, TInstant);
+ operation->AuthenticatedUser = YT_OPTIONAL_FROM_PROTO(protoOperation, authenticated_user);
if (protoOperation.has_brief_spec()) {
operation->BriefSpec = TYsonString(protoOperation.brief_spec());
@@ -860,11 +757,7 @@ void FromProto(NApi::TOperation* operation, const NProto::TOperation& protoOpera
operation->RuntimeParameters = TYsonString();
}
- if (protoOperation.has_suspended()) {
- operation->Suspended = protoOperation.suspended();
- } else {
- operation->Suspended.reset();
- }
+ operation->Suspended = YT_OPTIONAL_FROM_PROTO(protoOperation, suspended);
if (protoOperation.has_events()) {
operation->Events = TYsonString(protoOperation.events());
@@ -943,28 +836,16 @@ void ToProto(NProto::TJob* protoJob, const NApi::TJob& job)
protoJob->set_archive_state(ConvertJobStateToProto(*job.ArchiveState));
}
- if (job.StartTime) {
- protoJob->set_start_time(ToProto(*job.StartTime));
- }
- if (job.FinishTime) {
- protoJob->set_finish_time(ToProto(*job.FinishTime));
- }
+ YT_OPTIONAL_SET_PROTO(protoJob, start_time, job.StartTime);
+ YT_OPTIONAL_SET_PROTO(protoJob, finish_time, job.FinishTime);
- if (job.Address) {
- protoJob->set_address(*job.Address);
- }
+ YT_OPTIONAL_TO_PROTO(protoJob, address, job.Address);
if (job.Progress) {
protoJob->set_progress(*job.Progress);
}
- if (job.StderrSize) {
- protoJob->set_stderr_size(*job.StderrSize);
- }
- if (job.FailContextSize) {
- protoJob->set_fail_context_size(*job.FailContextSize);
- }
- if (job.HasSpec) {
- protoJob->set_has_spec(*job.HasSpec);
- }
+ YT_OPTIONAL_SET_PROTO(protoJob, stderr_size, job.StderrSize);
+ YT_OPTIONAL_SET_PROTO(protoJob, fail_context_size, job.FailContextSize);
+ YT_OPTIONAL_SET_PROTO(protoJob, has_spec, job.HasSpec);
if (job.Error) {
protoJob->set_error(job.Error.ToString());
@@ -988,39 +869,21 @@ void ToProto(NProto::TJob* protoJob, const NApi::TJob& job)
if (job.ProbingJobCompetitionId) {
ToProto(protoJob->mutable_probing_job_competition_id(), job.ProbingJobCompetitionId);
}
- if (job.HasCompetitors) {
- protoJob->set_has_competitors(*job.HasCompetitors);
- }
- if (job.HasProbingCompetitors) {
- protoJob->set_has_probing_competitors(*job.HasProbingCompetitors);
- }
- if (job.IsStale) {
- protoJob->set_is_stale(*job.IsStale);
- }
+ YT_OPTIONAL_SET_PROTO(protoJob, has_competitors, job.HasCompetitors);
+ YT_OPTIONAL_SET_PROTO(protoJob, has_probing_competitors, job.HasProbingCompetitors);
+ YT_OPTIONAL_SET_PROTO(protoJob, is_stale, job.IsStale);
if (job.ExecAttributes) {
protoJob->set_exec_attributes(job.ExecAttributes.ToString());
}
- if (job.TaskName) {
- protoJob->set_task_name(*job.TaskName);
- }
- if (job.PoolTree) {
- protoJob->set_pool_tree(*job.PoolTree);
- }
- if (job.Pool) {
- protoJob->set_pool(*job.Pool);
- }
- if (job.JobCookie) {
- protoJob->set_job_cookie(*job.JobCookie);
- }
+ YT_OPTIONAL_TO_PROTO(protoJob, task_name, job.TaskName);
+ YT_OPTIONAL_TO_PROTO(protoJob, pool_tree, job.PoolTree);
+ YT_OPTIONAL_TO_PROTO(protoJob, pool, job.Pool);
+ YT_OPTIONAL_SET_PROTO(protoJob, job_cookie, job.JobCookie);
if (job.ArchiveFeatures) {
protoJob->set_archive_features(job.ArchiveFeatures.ToString());
}
- if (job.MonitoringDescriptor) {
- protoJob->set_monitoring_descriptor(*job.MonitoringDescriptor);
- }
- if (job.OperationIncarnation) {
- protoJob->set_operation_incarnation(*job.OperationIncarnation);
- }
+ YT_OPTIONAL_TO_PROTO(protoJob, monitoring_descriptor, job.MonitoringDescriptor);
+ YT_OPTIONAL_SET_PROTO(protoJob, operation_incarnation, job.OperationIncarnation);
}
void FromProto(NApi::TJob* job, const NProto::TJob& protoJob)
@@ -1035,51 +898,15 @@ void FromProto(NApi::TJob* job, const NProto::TJob& protoJob)
} else {
job->OperationId = {};
}
- if (protoJob.has_type()) {
- job->Type = ConvertJobTypeFromProto(protoJob.type());
- } else {
- job->Type.reset();
- }
- if (protoJob.has_controller_state()) {
- job->ControllerState = ConvertJobStateFromProto(protoJob.controller_state());
- } else {
- job->ControllerState.reset();
- }
- if (protoJob.has_archive_state()) {
- job->ArchiveState = ConvertJobStateFromProto(protoJob.archive_state());
- } else {
- job->ArchiveState.reset();
- }
- if (protoJob.has_start_time()) {
- job->StartTime = TInstant::FromValue(protoJob.start_time());
- } else {
- job->StartTime.reset();
- }
- if (protoJob.has_finish_time()) {
- job->FinishTime = TInstant::FromValue(protoJob.finish_time());
- } else {
- job->FinishTime.reset();
- }
- if (protoJob.has_address()) {
- job->Address = protoJob.address();
- } else {
- job->Address.reset();
- }
- if (protoJob.has_progress()) {
- job->Progress = protoJob.progress();
- } else {
- job->Progress.reset();
- }
- if (protoJob.has_stderr_size()) {
- job->StderrSize = protoJob.stderr_size();
- } else {
- job->StderrSize.reset();
- }
- if (protoJob.has_fail_context_size()) {
- job->FailContextSize = protoJob.fail_context_size();
- } else {
- job->FailContextSize.reset();
- }
+ job->Type = YT_APPLY_PROTO_OPTIONAL(protoJob, type, ConvertJobTypeFromProto);
+ job->ControllerState = YT_APPLY_PROTO_OPTIONAL(protoJob, controller_state, ConvertJobStateFromProto);
+ job->ArchiveState = YT_APPLY_PROTO_OPTIONAL(protoJob, archive_state, ConvertJobStateFromProto);
+ job->StartTime = YT_OPTIONAL_FROM_PROTO(protoJob, start_time, TInstant);
+ job->FinishTime = YT_OPTIONAL_FROM_PROTO(protoJob, finish_time, TInstant);
+ job->Address = YT_OPTIONAL_FROM_PROTO(protoJob, address);
+ job->Progress = YT_OPTIONAL_FROM_PROTO(protoJob, progress);
+ job->StderrSize = YT_OPTIONAL_FROM_PROTO(protoJob, stderr_size);
+ job->FailContextSize = YT_OPTIONAL_FROM_PROTO(protoJob, fail_context_size);
if (protoJob.has_has_spec()) {
job->HasSpec = protoJob.has_spec();
} else {
@@ -1125,56 +952,24 @@ void FromProto(NApi::TJob* job, const NProto::TJob& protoJob)
} else {
job->HasCompetitors = false;
}
- if (protoJob.has_has_probing_competitors()) {
- job->HasProbingCompetitors = protoJob.has_probing_competitors();
- } else {
- job->HasProbingCompetitors = false;
- }
- if (protoJob.has_is_stale()) {
- job->IsStale = protoJob.is_stale();
- } else {
- job->IsStale.reset();
- }
+ job->HasProbingCompetitors = YT_OPTIONAL_FROM_PROTO(protoJob, has_probing_competitors);
+ job->IsStale = YT_OPTIONAL_FROM_PROTO(protoJob, is_stale);
if (protoJob.has_exec_attributes()) {
job->ExecAttributes = TYsonString(protoJob.exec_attributes());
} else {
job->ExecAttributes = TYsonString();
}
- if (protoJob.has_task_name()) {
- job->TaskName = protoJob.task_name();
- } else {
- job->TaskName.reset();
- }
- if (protoJob.has_pool_tree()) {
- job->PoolTree = protoJob.pool_tree();
- } else {
- job->PoolTree.reset();
- }
- if (protoJob.has_pool()) {
- job->Pool = protoJob.pool();
- } else {
- job->Pool.reset();
- }
- if (protoJob.has_job_cookie()) {
- job->JobCookie = protoJob.job_cookie();
- } else {
- job->JobCookie.reset();
- }
+ job->TaskName = YT_OPTIONAL_FROM_PROTO(protoJob, task_name);
+ job->PoolTree = YT_OPTIONAL_FROM_PROTO(protoJob, pool_tree);
+ job->Pool = YT_OPTIONAL_FROM_PROTO(protoJob, pool);
+ job->JobCookie = YT_OPTIONAL_FROM_PROTO(protoJob, job_cookie);
if (protoJob.has_archive_features()) {
job->ArchiveFeatures = TYsonString(protoJob.archive_features());
} else {
job->ArchiveFeatures = TYsonString();
}
- if (protoJob.has_monitoring_descriptor()) {
- job->MonitoringDescriptor = protoJob.monitoring_descriptor();
- } else {
- job->MonitoringDescriptor.reset();
- }
- if (protoJob.has_operation_incarnation()) {
- job->OperationIncarnation = protoJob.operation_incarnation();
- } else {
- job->OperationIncarnation.reset();
- }
+ job->MonitoringDescriptor = YT_OPTIONAL_FROM_PROTO(protoJob, monitoring_descriptor);
+ job->OperationIncarnation = YT_OPTIONAL_FROM_PROTO(protoJob, operation_incarnation);
}
void ToProto(
@@ -1265,21 +1060,15 @@ void ToProto(
protoStatistics->Clear();
ToProto(protoStatistics->mutable_column_data_weights(), statistics.ColumnDataWeights);
- if (statistics.TimestampTotalWeight) {
- protoStatistics->set_timestamp_total_weight(*statistics.TimestampTotalWeight);
- }
+ YT_OPTIONAL_SET_PROTO(protoStatistics, timestamp_total_weight, statistics.TimestampTotalWeight);
protoStatistics->set_legacy_chunk_data_weight(statistics.LegacyChunkDataWeight);
NYT::NTableClient::ToProto(protoStatistics->mutable_column_min_values(), statistics.ColumnMinValues);
NYT::NTableClient::ToProto(protoStatistics->mutable_column_max_values(), statistics.ColumnMaxValues);
ToProto(protoStatistics->mutable_column_non_null_value_counts(), statistics.ColumnNonNullValueCounts);
- if (statistics.ChunkRowCount) {
- protoStatistics->set_chunk_row_count(*statistics.ChunkRowCount);
- }
- if (statistics.LegacyChunkRowCount) {
- protoStatistics->set_legacy_chunk_row_count(*statistics.LegacyChunkRowCount);
- }
+ YT_OPTIONAL_SET_PROTO(protoStatistics, chunk_row_count, statistics.ChunkRowCount);
+ YT_OPTIONAL_SET_PROTO(protoStatistics, legacy_chunk_row_count, statistics.LegacyChunkRowCount);
ToProto(protoStatistics->mutable_column_hyperloglog_digests(), statistics.LargeStatistics.ColumnHyperLogLogDigests);
}
@@ -1289,27 +1078,15 @@ void FromProto(
const NProto::TColumnarStatistics& protoStatistics)
{
FromProto(&statistics->ColumnDataWeights, protoStatistics.column_data_weights());
- if (protoStatistics.has_timestamp_total_weight()) {
- statistics->TimestampTotalWeight = protoStatistics.timestamp_total_weight();
- } else {
- statistics->TimestampTotalWeight.reset();
- }
+ statistics->TimestampTotalWeight = YT_OPTIONAL_FROM_PROTO(protoStatistics, timestamp_total_weight);
statistics->LegacyChunkDataWeight = protoStatistics.legacy_chunk_data_weight();
NYT::NTableClient::FromProto(&statistics->ColumnMinValues, protoStatistics.column_min_values());
NYT::NTableClient::FromProto(&statistics->ColumnMaxValues, protoStatistics.column_max_values());
FromProto(&statistics->ColumnNonNullValueCounts, protoStatistics.column_non_null_value_counts());
- if (protoStatistics.has_chunk_row_count()) {
- statistics->ChunkRowCount = protoStatistics.chunk_row_count();
- } else {
- statistics->ChunkRowCount.reset();
- }
- if (protoStatistics.has_legacy_chunk_row_count()) {
- statistics->LegacyChunkRowCount = protoStatistics.legacy_chunk_row_count();
- } else {
- statistics->LegacyChunkRowCount.reset();
- }
+ statistics->ChunkRowCount = YT_OPTIONAL_FROM_PROTO(protoStatistics, chunk_row_count);
+ statistics->LegacyChunkRowCount = YT_OPTIONAL_FROM_PROTO(protoStatistics, legacy_chunk_row_count);
FromProto(&statistics->LargeStatistics.ColumnHyperLogLogDigests, protoStatistics.column_hyperloglog_digests());
}
@@ -1361,9 +1138,7 @@ void ToProto(
{
proto->set_max_row_count(result.MaxRowCount);
proto->set_max_data_weight(result.MaxDataWeight);
- if (result.DataWeightPerRowHint) {
- proto->set_data_weight_per_row_hint(*result.DataWeightPerRowHint);
- }
+ YT_OPTIONAL_SET_PROTO(proto, data_weight_per_row_hint, result.DataWeightPerRowHint);
}
void FromProto(
@@ -1372,9 +1147,7 @@ void FromProto(
{
result->MaxRowCount = proto.max_row_count();
result->MaxDataWeight = proto.max_data_weight();
- if (proto.has_data_weight_per_row_hint()) {
- result->DataWeightPerRowHint = proto.data_weight_per_row_hint();
- }
+ result->DataWeightPerRowHint = YT_OPTIONAL_FROM_PROTO(proto, data_weight_per_row_hint);
}
void ToProto(
@@ -1438,24 +1211,16 @@ void ToProto(
if (query.Engine) {
protoQuery->set_engine(ConvertQueryEngineToProto(*query.Engine));
}
- if (query.Query) {
- protoQuery->set_query(*query.Query);
- }
+ YT_OPTIONAL_TO_PROTO(protoQuery, query, query.Query);
if (query.Files) {
protoQuery->set_files(query.Files->ToString());
}
- if (query.StartTime) {
- protoQuery->set_start_time(NYT::ToProto(*query.StartTime));
- }
- if (query.FinishTime) {
- protoQuery->set_finish_time(NYT::ToProto(*query.FinishTime));
- }
+ YT_OPTIONAL_SET_PROTO(protoQuery, start_time, query.StartTime);
+ YT_OPTIONAL_SET_PROTO(protoQuery, finish_time, query.FinishTime);
if (query.Settings) {
protoQuery->set_settings(query.Settings.ToString());
}
- if (query.User) {
- protoQuery->set_user(*query.User);
- }
+ YT_OPTIONAL_TO_PROTO(protoQuery, user, query.User);
if (query.AccessControlObject) {
protoQuery->set_access_control_object(*query.AccessControlObject);
}
@@ -1464,15 +1229,11 @@ void ToProto(
if (query.State) {
protoQuery->set_state(ConvertQueryStateToProto(*query.State));
}
- if (query.ResultCount) {
- protoQuery->set_result_count(*query.ResultCount);
- }
+ YT_OPTIONAL_SET_PROTO(protoQuery, result_count, query.ResultCount);
if (query.Progress) {
protoQuery->set_progress(query.Progress.ToString());
}
- if (query.Error) {
- ToProto(protoQuery->mutable_error(), *query.Error);
- }
+ YT_OPTIONAL_TO_PROTO(protoQuery, error, query.Error);
if (query.Annotations) {
protoQuery->set_annotations(query.Annotations.ToString());
}
@@ -1487,71 +1248,27 @@ void FromProto(
{
FromProto(&query->Id, protoQuery.id());
- if (protoQuery.has_engine()) {
- query->Engine = ConvertQueryEngineFromProto(protoQuery.engine());
- } else {
- query->Engine.reset();
- }
- if (protoQuery.has_query()) {
- query->Query = protoQuery.query();
- } else {
- query->Query.reset();
- }
- if (protoQuery.has_files()) {
- query->Files = TYsonString(protoQuery.files());
- } else {
- query->Files.reset();
- }
- if (protoQuery.has_start_time()) {
- query->StartTime = TInstant::FromValue(protoQuery.start_time());
- } else {
- query->StartTime.reset();
- }
- if (protoQuery.has_finish_time()) {
- query->FinishTime = TInstant::FromValue(protoQuery.finish_time());
- } else {
- query->FinishTime.reset();
- }
+ query->Engine = YT_APPLY_PROTO_OPTIONAL(protoQuery, engine, ConvertQueryEngineFromProto);
+ query->Query = YT_OPTIONAL_FROM_PROTO(protoQuery, query);
+ query->Files = YT_APPLY_PROTO_OPTIONAL(protoQuery, files, TYsonString);
+ query->StartTime = YT_OPTIONAL_FROM_PROTO(protoQuery, start_time, TInstant);
+ query->FinishTime = YT_OPTIONAL_FROM_PROTO(protoQuery, finish_time, TInstant);
if (protoQuery.has_settings()) {
query->Settings = TYsonString(protoQuery.settings());
} else {
query->Settings = TYsonString{};
}
- if (protoQuery.has_user()) {
- query->User = protoQuery.user();
- } else {
- query->User.reset();
- }
- if (protoQuery.has_access_control_object()) {
- query->AccessControlObject = protoQuery.access_control_object();
- } else {
- query->AccessControlObject.reset();
- }
- if (protoQuery.has_access_control_objects()) {
- query->AccessControlObjects = TYsonString(protoQuery.access_control_objects());
- } else {
- query->AccessControlObjects.reset();
- }
- if (protoQuery.has_state()) {
- query->State = ConvertQueryStateFromProto(protoQuery.state());
- } else {
- query->State.reset();
- }
- if (protoQuery.has_result_count()) {
- query->ResultCount = protoQuery.result_count();
- } else {
- query->ResultCount.reset();
- }
+ query->User = YT_OPTIONAL_FROM_PROTO(protoQuery, user);
+ query->AccessControlObject = YT_OPTIONAL_FROM_PROTO(protoQuery, access_control_object);
+ query->AccessControlObjects = YT_APPLY_PROTO_OPTIONAL(protoQuery, access_control_objects, TYsonString);
+ query->State = YT_APPLY_PROTO_OPTIONAL(protoQuery, state, ConvertQueryStateFromProto);
+ query->ResultCount = YT_OPTIONAL_FROM_PROTO(protoQuery, result_count);
if (protoQuery.has_progress()) {
query->Progress = TYsonString(protoQuery.progress());
} else {
query->Progress = TYsonString{};
}
- if (protoQuery.has_error()) {
- query->Error = FromProto<TError>(protoQuery.error());
- } else {
- query->Error.reset();
- }
+ query->Error = YT_APPLY_PROTO_OPTIONAL(protoQuery, error, FromProto<TError>);
if (protoQuery.has_annotations()) {
query->Annotations = TYsonString(protoQuery.annotations());
} else {
diff --git a/yt/yt/client/api/rpc_proxy/table_mount_cache.cpp b/yt/yt/client/api/rpc_proxy/table_mount_cache.cpp
index d0a11cb943..8aaa7aeed3 100644
--- a/yt/yt/client/api/rpc_proxy/table_mount_cache.cpp
+++ b/yt/yt/client/api/rpc_proxy/table_mount_cache.cpp
@@ -103,8 +103,8 @@ private:
auto indexInfo = TIndexInfo{
.TableId = FromProto<NObjectClient::TObjectId>(protoIndexInfo.index_table_id()),
.Kind = FromProto<ESecondaryIndexKind>(protoIndexInfo.index_kind()),
- .Predicate = YT_PROTO_OPTIONAL(protoIndexInfo, predicate),
- .UnfoldedColumn = YT_PROTO_OPTIONAL(protoIndexInfo, unfolded_column),
+ .Predicate = YT_OPTIONAL_FROM_PROTO(protoIndexInfo, predicate),
+ .UnfoldedColumn = YT_OPTIONAL_FROM_PROTO(protoIndexInfo, unfolded_column),
.Correspondence = protoIndexInfo.has_index_correspondence()
? FromProto<ETableToIndexCorrespondence>(protoIndexInfo.index_correspondence())
: ETableToIndexCorrespondence::Unknown,
diff --git a/yt/yt/client/driver/etc_commands.cpp b/yt/yt/client/driver/etc_commands.cpp
index c1267fdcdb..debe2a3ffc 100644
--- a/yt/yt/client/driver/etc_commands.cpp
+++ b/yt/yt/client/driver/etc_commands.cpp
@@ -408,8 +408,9 @@ void TExecuteBatchCommand::DoExecute(ICommandContextPtr context)
void TDiscoverProxiesCommand::Register(TRegistrar registrar)
{
- registrar.Parameter("type", &TThis::Type)
- .Default(EProxyType::Rpc);
+ registrar.Parameter("kind", &TThis::Kind)
+ .Alias("type")
+ .Default(EProxyKind::Rpc);
registrar.Parameter("role", &TThis::Role)
.Default(DefaultRpcProxyRole);
registrar.Parameter("address_type", &TThis::AddressType)
@@ -423,11 +424,11 @@ void TDiscoverProxiesCommand::Register(TRegistrar registrar)
void TDiscoverProxiesCommand::DoExecute(ICommandContextPtr context)
{
TProxyDiscoveryRequest request{
- .Type = Type,
+ .Kind = Kind,
.Role = Role,
.AddressType = AddressType,
.NetworkName = NetworkName,
- .IgnoreBalancers = IgnoreBalancers
+ .IgnoreBalancers = IgnoreBalancers,
};
const auto& proxyDiscoveryCache = context->GetDriver()->GetProxyDiscoveryCache();
diff --git a/yt/yt/client/driver/etc_commands.h b/yt/yt/client/driver/etc_commands.h
index 789f8690d4..f51670a95e 100644
--- a/yt/yt/client/driver/etc_commands.h
+++ b/yt/yt/client/driver/etc_commands.h
@@ -230,7 +230,7 @@ public:
static void Register(TRegistrar registrar);
private:
- NApi::EProxyType Type;
+ NApi::EProxyKind Kind;
std::string Role;
NApi::NRpcProxy::EAddressType AddressType;
std::string NetworkName;
diff --git a/yt/yt/client/driver/proxy_discovery_cache.cpp b/yt/yt/client/driver/proxy_discovery_cache.cpp
index a9612bb359..e025f59105 100644
--- a/yt/yt/client/driver/proxy_discovery_cache.cpp
+++ b/yt/yt/client/driver/proxy_discovery_cache.cpp
@@ -26,7 +26,7 @@ using namespace NApi::NRpcProxy;
TProxyDiscoveryRequest::operator size_t() const
{
return MultiHash(
- Type,
+ Kind,
Role,
AddressType,
NetworkName,
@@ -37,8 +37,8 @@ TProxyDiscoveryRequest::operator size_t() const
void FormatValue(TStringBuilderBase* builder, const TProxyDiscoveryRequest& request, TStringBuf /*spec*/)
{
- builder->AppendFormat("{Type: %v, Role: %v, AddressType: %v, NetworkName: %v, IgnoreBalancers: %v}",
- request.Type,
+ builder->AppendFormat("{Kind: %v, Role: %v, AddressType: %v, NetworkName: %v, IgnoreBalancers: %v}",
+ request.Kind,
request.Role,
request.AddressType,
request.NetworkName,
@@ -97,7 +97,7 @@ private:
TYPath path;
try {
- path = GetProxyRegistryPath(request.Type) + "/@";
+ path = GetProxyRegistryPath(request.Kind) + "/@";
} catch (const std::exception& ex) {
YT_LOG_ERROR(ex, "Failed to get proxy registry path");
return MakeFuture<std::optional<TProxyDiscoveryResponse>>(ex);
@@ -130,7 +130,7 @@ private:
TYPath path;
try {
- path = GetProxyRegistryPath(request.Type);
+ path = GetProxyRegistryPath(request.Kind);
} catch (const std::exception& ex) {
YT_LOG_ERROR(ex, "Failed to get proxy registry path");
return MakeFuture<TProxyDiscoveryResponse>(ex);
@@ -168,12 +168,12 @@ private:
}
- static TYPath GetProxyRegistryPath(EProxyType type)
+ static TYPath GetProxyRegistryPath(EProxyKind type)
{
switch (type) {
- case EProxyType::Rpc:
+ case EProxyKind::Rpc:
return RpcProxiesPath;
- case EProxyType::Grpc:
+ case EProxyKind::Grpc:
return GrpcProxiesPath;
default:
THROW_ERROR_EXCEPTION("Proxy type %Qlv is not supported",
diff --git a/yt/yt/client/driver/proxy_discovery_cache.h b/yt/yt/client/driver/proxy_discovery_cache.h
index 9564dbdf67..814e440bd4 100644
--- a/yt/yt/client/driver/proxy_discovery_cache.h
+++ b/yt/yt/client/driver/proxy_discovery_cache.h
@@ -14,7 +14,7 @@ namespace NYT::NDriver {
struct TProxyDiscoveryRequest
{
- NApi::EProxyType Type;
+ NApi::EProxyKind Kind = NApi::EProxyKind::Rpc;
std::string Role = NApi::DefaultRpcProxyRole;
NApi::NRpcProxy::EAddressType AddressType = NApi::NRpcProxy::DefaultAddressType;
std::string NetworkName = NApi::NRpcProxy::DefaultNetworkName;
diff --git a/yt/yt/client/node_tracker_client/node_directory.cpp b/yt/yt/client/node_tracker_client/node_directory.cpp
index d3e08b7d84..c9bf77353d 100644
--- a/yt/yt/client/node_tracker_client/node_directory.cpp
+++ b/yt/yt/client/node_tracker_client/node_directory.cpp
@@ -344,31 +344,13 @@ void ToProto(NNodeTrackerClient::NProto::TNodeDescriptor* protoDescriptor, const
ToProto(protoDescriptor->mutable_addresses(), descriptor.Addresses());
- if (auto host = descriptor.GetHost()) {
- protoDescriptor->set_host(ToProto(*host));
- } else {
- protoDescriptor->clear_host();
- }
-
- if (auto rack = descriptor.GetRack()) {
- protoDescriptor->set_rack(ToProto(*rack));
- } else {
- protoDescriptor->clear_rack();
- }
-
- if (auto dataCenter = descriptor.GetDataCenter()) {
- protoDescriptor->set_data_center(ToProto(*dataCenter));
- } else {
- protoDescriptor->clear_data_center();
- }
+ YT_OPTIONAL_SET_PROTO(protoDescriptor, host, descriptor.GetHost());
+ YT_OPTIONAL_SET_PROTO(protoDescriptor, rack, descriptor.GetRack());
+ YT_OPTIONAL_SET_PROTO(protoDescriptor, data_center, descriptor.GetDataCenter());
ToProto(protoDescriptor->mutable_tags(), descriptor.GetTags());
- if (auto lastHeartbeatTime = descriptor.GetLastSeenTime()) {
- protoDescriptor->set_last_seen_time(ToProto(*lastHeartbeatTime));
- } else {
- protoDescriptor->clear_last_seen_time();
- }
+ YT_OPTIONAL_SET_PROTO(protoDescriptor, last_seen_time, descriptor.GetLastSeenTime());
}
void FromProto(NNodeTrackerClient::TNodeDescriptor* descriptor, const NNodeTrackerClient::NProto::TNodeDescriptor& protoDescriptor)
diff --git a/yt/yt/client/object_client/helpers.cpp b/yt/yt/client/object_client/helpers.cpp
index 0fbc5a8c9b..e7ba1b93ce 100644
--- a/yt/yt/client/object_client/helpers.cpp
+++ b/yt/yt/client/object_client/helpers.cpp
@@ -134,7 +134,8 @@ bool IsVersionedType(EObjectType type)
type == EObjectType::SequoiaMapNode ||
type == EObjectType::Pipeline ||
type == EObjectType::QueueConsumer ||
- type == EObjectType::QueueProducer;
+ type == EObjectType::QueueProducer ||
+ type == EObjectType::CypressProxyMap;
}
bool IsUserType(EObjectType type)
diff --git a/yt/yt/client/object_client/public.h b/yt/yt/client/object_client/public.h
index f534c40eac..5a7d177543 100644
--- a/yt/yt/client/object_client/public.h
+++ b/yt/yt/client/object_client/public.h
@@ -342,8 +342,10 @@ DEFINE_ENUM(EObjectType,
((ReplicationCardCollocation) (1207))
((VirtualChaosCellMap) (1208))
- // Maintenance tracker stuff
+ // Other cluster components stuff
((ClusterProxyNode) (1500))
+ ((CypressProxyObject) (1501))
+ ((CypressProxyMap) (465))
// Zookeeper stuff
// COMPAT(babenko): drop completely
diff --git a/yt/yt/client/security_client/public.h b/yt/yt/client/security_client/public.h
index 2bb6eaeffb..420aaab50b 100644
--- a/yt/yt/client/security_client/public.h
+++ b/yt/yt/client/security_client/public.h
@@ -76,12 +76,6 @@ YT_DEFINE_ERROR_ENUM(
((IrreversibleAclModification) (909))
);
-// NB: Changing this list requires reign promotion.
-DEFINE_ENUM(EProxyKind,
- ((Http) (1))
- ((Rpc) (2))
-);
-
DEFINE_ENUM(EAccessControlObjectNamespace,
(AdminCommands)
);
diff --git a/yt/yt/client/sequoia_client/public.h b/yt/yt/client/sequoia_client/public.h
index 5c213ebd9f..5411c0c770 100644
--- a/yt/yt/client/sequoia_client/public.h
+++ b/yt/yt/client/sequoia_client/public.h
@@ -7,9 +7,10 @@ namespace NYT::NSequoiaClient {
////////////////////////////////////////////////////////////////////////////////
YT_DEFINE_ERROR_ENUM(
- ((SequoiaClientNotReady) (6000))
- ((SequoiaTableCorrupted) (6001))
- ((SequoiaRetriableError) (6002))
+ ((SequoiaClientNotReady) (6000))
+ ((SequoiaTableCorrupted) (6001))
+ ((SequoiaRetriableError) (6002))
+ ((InvalidSequoiaReign) (6003))
);
////////////////////////////////////////////////////////////////////////////////
diff --git a/yt/yt/client/table_client/config.cpp b/yt/yt/client/table_client/config.cpp
index ec590507dc..b8bb96c164 100644
--- a/yt/yt/client/table_client/config.cpp
+++ b/yt/yt/client/table_client/config.cpp
@@ -289,6 +289,10 @@ void TDictionaryCompressionConfig::Register(TRegistrar registrar)
.Default(0.7)
.InRange(0, 1);
+ registrar.Parameter("elect_random_policy", &TThis::ElectRandomPolicy)
+ .Default(false)
+ .DontSerializeDefault();
+
registrar.Postprocessor([] (TThis* config) {
if (config->DesiredSampleCount > config->MaxProcessedSampleCount) {
THROW_ERROR_EXCEPTION("\"desired_sample_count\" cannot be greater than \"max_processed_sample_count\"");
diff --git a/yt/yt/client/table_client/config.h b/yt/yt/client/table_client/config.h
index 44eeba3b6b..fa096afa53 100644
--- a/yt/yt/client/table_client/config.h
+++ b/yt/yt/client/table_client/config.h
@@ -251,6 +251,9 @@ struct TDictionaryCompressionConfig
//! Upper limit on acceptable compression ratio. No chunk compression is performed if this limit is exceeded.
double MaxAcceptableCompressionRatio;
+ //! For testing purposes only.
+ bool ElectRandomPolicy;
+
REGISTER_YSON_STRUCT(TDictionaryCompressionConfig);
static void Register(TRegistrar registrar);
diff --git a/yt/yt/client/table_client/helpers.cpp b/yt/yt/client/table_client/helpers.cpp
index 680f5f995b..857893c458 100644
--- a/yt/yt/client/table_client/helpers.cpp
+++ b/yt/yt/client/table_client/helpers.cpp
@@ -164,7 +164,7 @@ void YTreeNodeToUnversionedValue(
} // namespace
TUnversionedOwningRow YsonToSchemafulRow(
- const TString& yson,
+ TStringBuf yson,
const TTableSchema& tableSchema,
bool treatMissingAsNull,
NYson::EYsonType ysonType,
@@ -172,8 +172,7 @@ TUnversionedOwningRow YsonToSchemafulRow(
{
auto nameTable = TNameTable::FromSchema(tableSchema);
- auto rowParts = ConvertTo<THashMap<TString, INodePtr>>(
- TYsonString(yson, ysonType));
+ auto rowParts = ConvertTo<THashMap<TString, INodePtr>>(TYsonString(yson, ysonType));
TUnversionedOwningRowBuilder rowBuilder;
auto validateAndAddValue = [&rowBuilder, &validateValues] (const TUnversionedValue& value, const TColumnSchema& column) {
@@ -266,7 +265,7 @@ TUnversionedOwningRow YsonToSchemafulRow(
return rowBuilder.FinishRow();
}
-TUnversionedOwningRow YsonToSchemalessRow(const TString& valueYson)
+TUnversionedOwningRow YsonToSchemalessRow(TStringBuf valueYson)
{
TUnversionedOwningRowBuilder builder;
@@ -285,8 +284,8 @@ TUnversionedOwningRow YsonToSchemalessRow(const TString& valueYson)
TVersionedRow YsonToVersionedRow(
const TRowBufferPtr& rowBuffer,
- const TString& keyYson,
- const TString& valueYson,
+ TStringBuf keyYson,
+ TStringBuf valueYson,
const std::vector<TTimestamp>& deleteTimestamps,
const std::vector<TTimestamp>& extraWriteTimestamps)
{
@@ -349,8 +348,8 @@ TVersionedRow YsonToVersionedRow(
}
TVersionedOwningRow YsonToVersionedRow(
- const TString& keyYson,
- const TString& valueYson,
+ TStringBuf keyYson,
+ TStringBuf valueYson,
const std::vector<TTimestamp>& deleteTimestamps,
const std::vector<TTimestamp>& extraWriteTimestamps)
{
@@ -360,7 +359,7 @@ TVersionedOwningRow YsonToVersionedRow(
return TVersionedOwningRow(row);
}
-TUnversionedOwningRow YsonToKey(const TString& yson)
+TUnversionedOwningRow YsonToKey(TStringBuf yson)
{
TUnversionedOwningRowBuilder keyBuilder;
auto keyParts = ConvertTo<std::vector<INodePtr>>(
diff --git a/yt/yt/client/table_client/helpers.h b/yt/yt/client/table_client/helpers.h
index d1c5d17325..36bbff3498 100644
--- a/yt/yt/client/table_client/helpers.h
+++ b/yt/yt/client/table_client/helpers.h
@@ -34,25 +34,24 @@ NChunkClient::EChunkFormat DefaultFormatFromOptimizeFor(
// Mostly used in unittests and for debugging purposes.
// Quite inefficient.
TUnversionedOwningRow YsonToSchemafulRow(
- const TString& yson,
+ TStringBuf yson,
const TTableSchema& tableSchema,
bool treatMissingAsNull,
NYson::EYsonType ysonType = NYson::EYsonType::MapFragment,
bool validateValues = false);
-TUnversionedOwningRow YsonToSchemalessRow(
- const TString& yson);
+TUnversionedOwningRow YsonToSchemalessRow(TStringBuf yson);
TVersionedRow YsonToVersionedRow(
const TRowBufferPtr& rowBuffer,
- const TString& keyYson,
- const TString& valueYson,
+ TStringBuf keyYson,
+ TStringBuf valueYson,
const std::vector<TTimestamp>& deleteTimestamps = {},
const std::vector<TTimestamp>& extraWriteTimestamps = {});
TVersionedOwningRow YsonToVersionedRow(
- const TString& keyYson,
- const TString& valueYson,
+ TStringBuf keyYson,
+ TStringBuf valueYson,
const std::vector<TTimestamp>& deleteTimestamps = {},
const std::vector<TTimestamp>& extraWriteTimestamps = {});
-TUnversionedOwningRow YsonToKey(const TString& yson);
+TUnversionedOwningRow YsonToKey(TStringBuf yson);
TString KeyToYson(TUnversionedRow row);
////////////////////////////////////////////////////////////////////////////////
diff --git a/yt/yt/client/table_client/schema.cpp b/yt/yt/client/table_client/schema.cpp
index 7e28fd9830..a7715d96db 100644
--- a/yt/yt/client/table_client/schema.cpp
+++ b/yt/yt/client/table_client/schema.cpp
@@ -429,41 +429,13 @@ void ToProto(NProto::TColumnSchema* protoSchema, const TColumnSchema& schema)
protoSchema->clear_required();
}
ToProto(protoSchema->mutable_logical_type(), schema.LogicalType());
- if (schema.Lock()) {
- protoSchema->set_lock(ToProto(*schema.Lock()));
- } else {
- protoSchema->clear_lock();
- }
- if (schema.Expression()) {
- protoSchema->set_expression(*schema.Expression());
- } else {
- protoSchema->clear_expression();
- }
- if (schema.Materialized()) {
- protoSchema->set_materialized(*schema.Materialized());
- } else {
- protoSchema->clear_materialized();
- }
- if (schema.Aggregate()) {
- protoSchema->set_aggregate(ToProto(*schema.Aggregate()));
- } else {
- protoSchema->clear_aggregate();
- }
- if (schema.SortOrder()) {
- protoSchema->set_sort_order(ToProto(*schema.SortOrder()));
- } else {
- protoSchema->clear_sort_order();
- }
- if (schema.Group()) {
- protoSchema->set_group(ToProto(*schema.Group()));
- } else {
- protoSchema->clear_group();
- }
- if (schema.MaxInlineHunkSize()) {
- protoSchema->set_max_inline_hunk_size(*schema.MaxInlineHunkSize());
- } else {
- protoSchema->clear_max_inline_hunk_size();
- }
+ YT_OPTIONAL_SET_PROTO(protoSchema, lock, schema.Lock());
+ YT_OPTIONAL_TO_PROTO(protoSchema, expression, schema.Expression());
+ YT_OPTIONAL_SET_PROTO(protoSchema, materialized, schema.Materialized());
+ YT_OPTIONAL_SET_PROTO(protoSchema, aggregate, schema.Aggregate());
+ YT_OPTIONAL_SET_PROTO(protoSchema, sort_order, schema.SortOrder());
+ YT_OPTIONAL_SET_PROTO(protoSchema, group, schema.Group());
+ YT_OPTIONAL_SET_PROTO(protoSchema, max_inline_hunk_size, schema.MaxInlineHunkSize());
}
void ToProto(NProto::TDeletedColumn* protoSchema, const TDeletedColumn& schema)
@@ -492,13 +464,13 @@ void FromProto(TColumnSchema* schema, const NProto::TColumnSchema& protoSchema)
schema->SetLogicalType(MakeLogicalType(GetLogicalType(physicalType), protoSchema.required()));
}
- schema->SetLock(YT_PROTO_OPTIONAL(protoSchema, lock));
- schema->SetExpression(YT_PROTO_OPTIONAL(protoSchema, expression));
- schema->SetMaterialized(YT_PROTO_OPTIONAL(protoSchema, materialized));
- schema->SetAggregate(YT_PROTO_OPTIONAL(protoSchema, aggregate));
+ schema->SetLock(YT_OPTIONAL_FROM_PROTO(protoSchema, lock));
+ schema->SetExpression(YT_OPTIONAL_FROM_PROTO(protoSchema, expression));
+ schema->SetMaterialized(YT_OPTIONAL_FROM_PROTO(protoSchema, materialized));
+ schema->SetAggregate(YT_OPTIONAL_FROM_PROTO(protoSchema, aggregate));
schema->SetSortOrder(YT_APPLY_PROTO_OPTIONAL(protoSchema, sort_order, FromProto<ESortOrder>));
- schema->SetGroup(YT_PROTO_OPTIONAL(protoSchema, group));
- schema->SetMaxInlineHunkSize(YT_PROTO_OPTIONAL(protoSchema, max_inline_hunk_size));
+ schema->SetGroup(YT_OPTIONAL_FROM_PROTO(protoSchema, group));
+ schema->SetMaxInlineHunkSize(YT_OPTIONAL_FROM_PROTO(protoSchema, max_inline_hunk_size));
}
void FromProto(TDeletedColumn* schema, const NProto::TDeletedColumn& protoSchema)
diff --git a/yt/yt/core/concurrency/fls-inl.h b/yt/yt/core/concurrency/fls-inl.h
index 6267f7f054..a192a1dcb0 100644
--- a/yt/yt/core/concurrency/fls-inl.h
+++ b/yt/yt/core/concurrency/fls-inl.h
@@ -93,6 +93,12 @@ Y_FORCE_INLINE T* TFlsSlot<T>::GetOrCreate() const
}
template <class T>
+Y_FORCE_INLINE T* TFlsSlot<T>::MaybeGet() const
+{
+ return static_cast<T*>(GetCurrentFls()->Get(Index_));
+}
+
+template <class T>
T* TFlsSlot<T>::Create() const
{
auto cookie = new T();
diff --git a/yt/yt/core/concurrency/fls.h b/yt/yt/core/concurrency/fls.h
index 616fd7d590..9306ffeeb8 100644
--- a/yt/yt/core/concurrency/fls.h
+++ b/yt/yt/core/concurrency/fls.h
@@ -42,6 +42,7 @@ public:
T* operator->();
T* GetOrCreate() const;
+ T* MaybeGet() const;
const T* Get(const TFls& fls) const;
bool IsInitialized() const;
diff --git a/yt/yt/core/misc/error.cpp b/yt/yt/core/misc/error.cpp
index fb0b4d193f..99d8ed9161 100644
--- a/yt/yt/core/misc/error.cpp
+++ b/yt/yt/core/misc/error.cpp
@@ -1,10 +1,11 @@
#include "error.h"
#include "serialize.h"
-#include <yt/yt/core/concurrency/public.h>
+#include <yt/yt/core/concurrency/fls.h>
#include <yt/yt/core/net/local_address.h>
+#include <yt/yt/core/misc/collection_helpers.h>
#include <yt/yt/core/misc/protobuf_helpers.h>
#include <yt/yt/core/tracing/trace_context.h>
@@ -18,6 +19,8 @@
#include <library/cpp/yt/global/variable.h>
+#include <library/cpp/yt/misc/global.h>
+
namespace NYT {
using namespace NTracing;
@@ -31,6 +34,8 @@ using NYT::ToProto;
constexpr TStringBuf OriginalErrorDepthAttribute = "original_error_depth";
+bool TErrorCodicils::Initialized_ = false;
+
////////////////////////////////////////////////////////////////////////////////
namespace NDetail {
@@ -656,4 +661,96 @@ void TErrorSerializer::Load(TStreamLoadContext& context, TError& error)
////////////////////////////////////////////////////////////////////////////////
+static YT_DEFINE_GLOBAL(NConcurrency::TFlsSlot<TErrorCodicils>, ErrorCodicilsSlot);
+
+TErrorCodicils::TGuard::~TGuard()
+{
+ TErrorCodicils::GetOrCreate().Set(std::move(Key_), std::move(OldGetter_));
+}
+
+TErrorCodicils::TGuard::TGuard(
+ std::string key,
+ TGetter oldGetter)
+ : Key_(std::move(key))
+ , OldGetter_(std::move(oldGetter))
+{ }
+
+void TErrorCodicils::Initialize()
+{
+ if (Initialized_) {
+ // Multiple calls are OK.
+ return;
+ }
+ Initialized_ = true;
+
+ ErrorCodicilsSlot(); // Warm up the slot.
+ TError::RegisterEnricher([] (TError& error) {
+ if (auto* codicils = TErrorCodicils::MaybeGet()) {
+ codicils->Apply(error);
+ }
+ });
+}
+
+TErrorCodicils& TErrorCodicils::GetOrCreate()
+{
+ return *ErrorCodicilsSlot().GetOrCreate();
+}
+
+TErrorCodicils* TErrorCodicils::MaybeGet()
+{
+ return ErrorCodicilsSlot().MaybeGet();
+}
+
+std::optional<std::string> TErrorCodicils::MaybeEvaluate(const std::string& key)
+{
+ auto* instance = MaybeGet();
+ if (!instance) {
+ return {};
+ }
+
+ auto getter = instance->Get(key);
+ if (!getter) {
+ return {};
+ }
+
+ return getter();
+}
+
+auto TErrorCodicils::Guard(std::string key, TGetter getter) -> TGuard
+{
+ auto& instance = GetOrCreate();
+ auto [it, added] = instance.Getters_.try_emplace(key, getter);
+ TGetter oldGetter;
+ if (!added) {
+ oldGetter = std::move(it->second);
+ it->second = std::move(getter);
+ }
+ return TGuard(std::move(key), std::move(oldGetter));
+}
+
+void TErrorCodicils::Apply(TError& error) const
+{
+ for (const auto& [key, getter] : Getters_) {
+ error <<= TErrorAttribute(key, getter());
+ }
+}
+
+void TErrorCodicils::Set(std::string key, TGetter getter)
+{
+ // We could enforce Initialized_, but that could make an error condition worse at runtime.
+ // Instead, let's keep enrichment optional.
+ if (getter) {
+ Getters_.insert_or_assign(std::move(key), std::move(getter));
+ } else {
+ Getters_.erase(std::move(key));
+ }
+}
+
+auto TErrorCodicils::Get(const std::string& key) const -> TGetter
+{
+ return GetOrDefault(Getters_, std::move(key));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
} // namespace NYT
diff --git a/yt/yt/core/misc/error.h b/yt/yt/core/misc/error.h
index fd47419ee9..b53da2cf1d 100644
--- a/yt/yt/core/misc/error.h
+++ b/yt/yt/core/misc/error.h
@@ -13,6 +13,8 @@
#include <library/cpp/yt/error/error.h>
#include <library/cpp/yt/error/origin_attributes.h>
+#include <util/generic/noncopyable.h>
+
namespace NYT {
////////////////////////////////////////////////////////////////////////////////
@@ -90,6 +92,56 @@ struct TSerializerTraits<
////////////////////////////////////////////////////////////////////////////////
+// A fiber-local set of attributes to enrich all errors with.
+class TErrorCodicils
+{
+public:
+ using TGetter = std::function<std::string()>;
+
+ class TGuard
+ : public TNonCopyable
+ {
+ public:
+ ~TGuard();
+
+ private:
+ friend class TErrorCodicils;
+ TGuard(std::string key, TGetter oldGetter);
+ std::string Key_;
+ TGetter OldGetter_;
+ };
+
+ // Call from single-threaded bootstrapping code. Errors will not be enriched otherwise.
+ static void Initialize();
+
+ // Gets or creates an instance for this fiber.
+ static TErrorCodicils& GetOrCreate();
+
+ // Gets the instance for this fiber if one was created previously.
+ static TErrorCodicils* MaybeGet();
+
+ // Evaluates the codicil for the key if one was set.
+ static std::optional<std::string> MaybeEvaluate(const std::string& key);
+
+ // Sets the getter and returns an RAII object to restore the previous value on desctruction.
+ static TGuard Guard(std::string key, TGetter getter);
+
+ // Adds error attributes.
+ void Apply(TError& error) const;
+
+ // Sets the getter (or erases if the getter is empty).
+ void Set(std::string key, TGetter getter);
+
+ // Gets the getter.
+ TGetter Get(const std::string& key) const;
+
+private:
+ THashMap<std::string, TGetter> Getters_;
+ static bool Initialized_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
} // namespace NYT
#define ERROR_INL_H_
diff --git a/yt/yt/core/misc/protobuf_helpers-inl.h b/yt/yt/core/misc/protobuf_helpers-inl.h
index 4ad0fb2272..ed4548cfe4 100644
--- a/yt/yt/core/misc/protobuf_helpers-inl.h
+++ b/yt/yt/core/misc/protobuf_helpers-inl.h
@@ -47,7 +47,7 @@ DEFINE_TRIVIAL_PROTO_CONVERSIONS(bool)
////////////////////////////////////////////////////////////////////////////////
-#define YT_PROTO_OPTIONAL_CONVERT(...) __VA_OPT__(::NYT::FromProto<__VA_ARGS__>)
+#define YT_OPTIONAL_FROM_PROTO_CONVERT(...) __VA_OPT__(::NYT::FromProto<__VA_ARGS__>)
////////////////////////////////////////////////////////////////////////////////
diff --git a/yt/yt/core/misc/protobuf_helpers.h b/yt/yt/core/misc/protobuf_helpers.h
index 050774bff7..c25d7c2902 100644
--- a/yt/yt/core/misc/protobuf_helpers.h
+++ b/yt/yt/core/misc/protobuf_helpers.h
@@ -464,14 +464,34 @@ google::protobuf::Timestamp GetProtoNow();
//! field. Macro accepts desired target type as optional third parameter.
//! Usage:
//! // Get as is.
-//! int instantInt = YT_PROTO_OPTIONAL(message, instant);
+//! int instantInt = YT_OPTIONAL_FROM_PROTO(message, instant);
//! // Get with conversion.
-//! TInstant instant = YT_PROTO_OPTIONAL(message, instant, TInstant);
-#define YT_PROTO_OPTIONAL(message, field, ...) \
+//! TInstant instant = YT_OPTIONAL_FROM_PROTO(message, instant, TInstant);
+#define YT_OPTIONAL_FROM_PROTO(message, field, ...) \
(((message).has_##field()) \
- ? std::optional(YT_PROTO_OPTIONAL_CONVERT(__VA_ARGS__)((message).field())) \
+ ? std::optional(YT_OPTIONAL_FROM_PROTO_CONVERT(__VA_ARGS__)((message).field())) \
: std::nullopt)
+#define YT_OPTIONAL_TO_PROTO(message, field, original) \
+ do {\
+ if (original.has_value()) {\
+ ToProto((message)->mutable_##field(), *original);\
+ } else {\
+ (message)->clear_##field();\
+ }\
+ } while (false)
+
+#define YT_OPTIONAL_SET_PROTO(message, field, original) \
+ do {\
+ /* Avoid unnecessary computation if <original> is a return value of a call*/\
+ const auto& originalRef = (original);\
+ if (originalRef.has_value()) {\
+ (message)->set_##field(ToProto(*originalRef));\
+ } else {\
+ (message)->clear_##field();\
+ }\
+ } while (false)
+
// TODO(cherepashka): to remove after std::optional::and_then is here.
//! This macro may be used to extract std::optional<T> from protobuf message field of type T and to apply some function to value if it is present.
#define YT_APPLY_PROTO_OPTIONAL(message, field, function) (((message).has_##field()) ? std::make_optional(function((message).field())) : std::nullopt)
diff --git a/yt/yt/core/misc/unittests/error_ut.cpp b/yt/yt/core/misc/unittests/error_ut.cpp
index 0c6d32d08c..6f6c3c2efe 100644
--- a/yt/yt/core/misc/unittests/error_ut.cpp
+++ b/yt/yt/core/misc/unittests/error_ut.cpp
@@ -228,6 +228,20 @@ TEST(TErrorTest, NativeFiberId)
.ThrowOnError();
}
+TEST(TErrorTest, ErrorCodicils)
+{
+ EXPECT_FALSE(TError("ErrorCodicils").Attributes().Contains("test_attribute"));
+ {
+ auto guard = TErrorCodicils::Guard("test_attribute", [] () -> std::string {
+ return "test_value";
+ });
+ EXPECT_EQ("test_value",
+ TError("ErrorCodicils").Attributes().Get<std::string>("test_attribute"));
+ EXPECT_FALSE(TError().Attributes().Contains("test_attribute"));
+ }
+ EXPECT_FALSE(TError("ErrorCodicils").Attributes().Contains("test_attribute"));
+}
+
////////////////////////////////////////////////////////////////////////////////
} // namespace
diff --git a/yt/yt/core/test_framework/framework.cpp b/yt/yt/core/test_framework/framework.cpp
index 15df3840cb..a5c6f709ad 100644
--- a/yt/yt/core/test_framework/framework.cpp
+++ b/yt/yt/core/test_framework/framework.cpp
@@ -156,6 +156,8 @@ Y_TEST_HOOK_BEFORE_RUN(GTEST_YT_SETUP)
NYT::TSignalRegistry::Get()->PushDefaultSignalHandler(NYT::AllCrashSignals);
#endif
+ NYT::TErrorCodicils::Initialize();
+
auto config = NYT::NLogging::TLogManagerConfig::CreateYTServer("unittester", GetOutputPath().GetPath());
NYT::NLogging::TLogManager::Get()->Configure(config);
NYT::NLogging::TLogManager::Get()->EnableReopenOnSighup();
diff --git a/yt/yt/core/ytree/node_detail.cpp b/yt/yt/core/ytree/node_detail.cpp
index dd7ea9f3ea..8f7d770f27 100644
--- a/yt/yt/core/ytree/node_detail.cpp
+++ b/yt/yt/core/ytree/node_detail.cpp
@@ -314,7 +314,7 @@ void TMapNodeMixin::ListSelf(
? FromProto<TAttributeFilter>(request->attributes())
: TAttributeFilter();
- auto limit = YT_PROTO_OPTIONAL(*request, limit);
+ auto limit = YT_OPTIONAL_FROM_PROTO(*request, limit);
context->SetRequestInfo("Limit: %v, AttributeFilter: %v",
limit,
diff --git a/yt/yt/core/ytree/request_complexity_limits.cpp b/yt/yt/core/ytree/request_complexity_limits.cpp
index faea8bdc32..bb3834123a 100644
--- a/yt/yt/core/ytree/request_complexity_limits.cpp
+++ b/yt/yt/core/ytree/request_complexity_limits.cpp
@@ -41,8 +41,8 @@ void FromProto(
TReadRequestComplexityOverrides* original,
const NProto::TReadRequestComplexityLimits& serialized)
{
- original->NodeCount = YT_PROTO_OPTIONAL(serialized, node_count);
- original->ResultSize = YT_PROTO_OPTIONAL(serialized, result_size);
+ original->NodeCount = YT_OPTIONAL_FROM_PROTO(serialized, node_count);
+ original->ResultSize = YT_OPTIONAL_FROM_PROTO(serialized, result_size);
}
void ToProto(
diff --git a/yt/yt/core/ytree/unittests/yson_struct_update_ut.cpp b/yt/yt/core/ytree/unittests/yson_struct_update_ut.cpp
index 0fb160fdcd..fd82265dd7 100644
--- a/yt/yt/core/ytree/unittests/yson_struct_update_ut.cpp
+++ b/yt/yt/core/ytree/unittests/yson_struct_update_ut.cpp
@@ -176,5 +176,36 @@ TEST(TUpdateYsonStructTest, Nested)
EXPECT_EQ(updatedCommand, "sort");
}
+TEST(TUpdateYsonStructTest, Validate)
+{
+ auto oldSpec = ConvertTo<TSpecWithPoolPtr>(TYsonString(TString("{pool=pool;}")));
+ auto longPoolSpec = ConvertTo<TSpecWithPoolPtr>(TYsonString(TString("{pool=new_pool;}")));
+ auto shortPoolSpec = ConvertTo<TSpecWithPoolPtr>(TYsonString(TString("{pool=p;}")));
+
+ std::string updatedPool;
+
+ auto configurator = TConfigurator<TSpecWithPool>();
+ configurator.Field("pool", &TSpecWithPool::Pool)
+ .Validator(BIND([&] (const std::string& newPool) {
+ THROW_ERROR_EXCEPTION_IF(
+ newPool.size() > 4,
+ "Pool name too long");
+ }))
+ .Updater(BIND([&] (const std::string& newPool) {
+ updatedPool = newPool;
+ }));
+
+ auto sealed = std::move(configurator).Seal();
+
+ EXPECT_THROW_WITH_SUBSTRING(
+ sealed.Validate(oldSpec, longPoolSpec),
+ "Pool name too long");
+
+ sealed.Validate(oldSpec, shortPoolSpec);
+ sealed.Update(oldSpec, shortPoolSpec);
+
+ EXPECT_EQ(updatedPool, "p");
+}
+
} // namespace
} // namespace NYT::NYTree
diff --git a/yt/yt/core/ytree/yson_struct_update-inl.h b/yt/yt/core/ytree/yson_struct_update-inl.h
index 3ba88ec853..c6ae5f1ead 100644
--- a/yt/yt/core/ytree/yson_struct_update-inl.h
+++ b/yt/yt/core/ytree/yson_struct_update-inl.h
@@ -49,9 +49,27 @@ struct TUnwrapYsonStructIntrusivePtr<TIntrusivePtr<T>>
////////////////////////////////////////////////////////////////////////////////
template <class TValue>
+TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Validator(TCallback<void(const TValue&, const TValue&)> validator)
+{
+ VerifyEmptyValidator();
+ Validator_ = validator;
+ return *this;
+}
+
+template <class TValue>
+TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Validator(TCallback<void(const TValue&)> validator)
+{
+ VerifyEmptyValidator();
+ Validator_ = BIND_NO_PROPAGATE([validator = std::move(validator)] (const TValue& /*oldValue*/, const TValue& newValue) {
+ validator(std::move(newValue));
+ });
+ return *this;
+}
+
+template <class TValue>
TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Updater(TCallback<void(const TValue&, const TValue&)> updater)
{
- VerifyEmpty();
+ VerifyEmptyUpdater();
Updater_ = updater;
return *this;
}
@@ -59,7 +77,7 @@ TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Updater(TCallback<void(const T
template <class TValue>
TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::Updater(TCallback<void(const TValue&)> updater)
{
- VerifyEmpty();
+ VerifyEmptyUpdater();
Updater_ = BIND_NO_PROPAGATE([updater = std::move(updater)] (const TValue& /*oldValue*/, const TValue& newValue) {
updater(std::move(newValue));
});
@@ -90,7 +108,7 @@ TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::NestedUpdater(
TUnwrappedValue,
typename NDetail::TUnwrapYsonStructIntrusivePtr<TValue>::TStruct>);
- VerifyEmpty();
+ VerifyEmptyUpdater();
auto configurator = configureCallback();
Updater_ = BIND_NO_PROPAGATE([configurator = std::move(configurator)] (const TValue& oldValue, const TValue& newValue) {
configurator.Update(oldValue, newValue);
@@ -99,6 +117,23 @@ TFieldRegistrar<TValue>& TFieldRegistrar<TValue>::NestedUpdater(
}
template <class TValue>
+void TFieldRegistrar<TValue>::DoValidate(
+ IYsonStructParameterPtr parameter,
+ TYsonStructBase* oldStruct,
+ TYsonStructBase* newStruct) const
+{
+ if (!Validator_) {
+ return;
+ }
+
+ auto typedParameter = DynamicPointerCast<TYsonStructParameter<TValue>>(parameter);
+ YT_VERIFY(typedParameter);
+ Validator_(
+ typedParameter->GetValue(oldStruct),
+ typedParameter->GetValue(newStruct));
+}
+
+template <class TValue>
void TFieldRegistrar<TValue>::DoUpdate(
IYsonStructParameterPtr parameter,
TYsonStructBase* oldStruct,
@@ -116,7 +151,13 @@ void TFieldRegistrar<TValue>::DoUpdate(
}
template <class TValue>
-void TFieldRegistrar<TValue>::VerifyEmpty() const
+void TFieldRegistrar<TValue>::VerifyEmptyValidator() const
+{
+ YT_VERIFY(!Validator_);
+}
+
+template <class TValue>
+void TFieldRegistrar<TValue>::VerifyEmptyUpdater() const
{
YT_VERIFY(!Updater_);
}
@@ -169,6 +210,7 @@ TSealedConfigurator<TStruct> TConfigurator<TStruct>::Seal() &&
{
return std::move(*this);
}
+
////////////////////////////////////////////////////////////////////////////////
template <CYsonStructDerived TStruct>
@@ -177,10 +219,27 @@ TSealedConfigurator<TStruct>::TSealedConfigurator(TConfigurator<TStruct> configu
{ }
template <CYsonStructDerived TStruct>
+void TSealedConfigurator<TStruct>::Validate(
+ TIntrusivePtr<TStruct> oldStruct,
+ TIntrusivePtr<TStruct> newStruct) const
+{
+ Do(oldStruct, newStruct, &NDetail::IFieldRegistrar::DoValidate);
+}
+
+template <CYsonStructDerived TStruct>
void TSealedConfigurator<TStruct>::Update(
TIntrusivePtr<TStruct> oldStruct,
TIntrusivePtr<TStruct> newStruct) const
{
+ Do(oldStruct, newStruct, &NDetail::IFieldRegistrar::DoUpdate);
+}
+
+template <CYsonStructDerived TStruct>
+void TSealedConfigurator<TStruct>::Do(
+ TIntrusivePtr<TStruct> oldStruct,
+ TIntrusivePtr<TStruct> newStruct,
+ TFieldRegistrarMethod fieldMethod) const
+{
const auto* meta = oldStruct->GetMeta();
YT_VERIFY(meta == newStruct->GetMeta());
const auto& parameterToFieldRegistrar = RegisteredFields_->ParameterToFieldRegistrar;
@@ -194,7 +253,7 @@ void TSealedConfigurator<TStruct>::Update(
if (fieldDescIter == parameterToFieldRegistrar.end()) {
THROW_ERROR_EXCEPTION("Field %Qv is not marked as updatable, but was changed", name);
} else {
- fieldDescIter->second->DoUpdate(parameter, oldStruct.Get(), newStruct.Get());
+ (*(fieldDescIter->second).*fieldMethod)(parameter, oldStruct.Get(), newStruct.Get());
}
}
}
diff --git a/yt/yt/core/ytree/yson_struct_update.h b/yt/yt/core/ytree/yson_struct_update.h
index 4b472021d1..7a517d6f57 100644
--- a/yt/yt/core/ytree/yson_struct_update.h
+++ b/yt/yt/core/ytree/yson_struct_update.h
@@ -26,6 +26,11 @@ DECLARE_REFCOUNTED_STRUCT(TRegisteredFieldDirectory);
struct IFieldRegistrar
: public TRefCounted
{
+ virtual void DoValidate(
+ IYsonStructParameterPtr parameter,
+ TYsonStructBase* oldStruct,
+ TYsonStructBase* newStruct) const = 0;
+
virtual void DoUpdate(
IYsonStructParameterPtr parameter,
TYsonStructBase* oldStruct,
@@ -42,6 +47,12 @@ class TFieldRegistrar
: public IFieldRegistrar
{
public:
+ // Registers validator that accepts old and new values as arguments.
+ TFieldRegistrar& Validator(TCallback<void(const TValue&, const TValue&)> validator);
+
+ // Registers validator that accepts only new value as an argument.
+ TFieldRegistrar& Validator(TCallback<void(const TValue&)> validator);
+
// Registers updater that accepts old and new values as arguments.
TFieldRegistrar& Updater(TCallback<void(const TValue&, const TValue&)> updater);
@@ -58,10 +69,17 @@ public:
TYsonStructBase* oldStruct,
TYsonStructBase* newStruct) const override;
+ void DoValidate(
+ IYsonStructParameterPtr parameter,
+ TYsonStructBase* oldStruct,
+ TYsonStructBase* newStruct) const override;
+
private:
- void VerifyEmpty() const;
+ void VerifyEmptyUpdater() const;
+ void VerifyEmptyValidator() const;
TCallback<void(const TValue&, const TValue&)> Updater_;
+ TCallback<void(const TValue&, const TValue&)> Validator_;
};
////////////////////////////////////////////////////////////////////////////////
@@ -114,11 +132,25 @@ class TSealedConfigurator
public:
TSealedConfigurator(TConfigurator<TStruct> configurator);
+ void Validate(
+ TIntrusivePtr<TStruct> oldStruct,
+ TIntrusivePtr<TStruct> newStruct) const;
+
void Update(
TIntrusivePtr<TStruct> oldStruct,
TIntrusivePtr<TStruct> newStruct) const;
private:
+ using TFieldRegistrarMethod = void(NDetail::IFieldRegistrar::*)(
+ IYsonStructParameterPtr parameter,
+ TYsonStructBase* oldStruct,
+ TYsonStructBase* newStruct) const;
+
+ void Do(
+ TIntrusivePtr<TStruct> oldStruct,
+ TIntrusivePtr<TStruct> newStruct,
+ TFieldRegistrarMethod fieldMethod) const;
+
NDetail::TRegisteredFieldDirectoryPtr RegisteredFields_;
};