From c3f0ac2a142137c45802be21f38921ef2a3d702c Mon Sep 17 00:00:00 2001 From: BayoNet Date: Wed, 5 Sep 2018 17:04:46 +0300 Subject: [PATCH 01/78] Preparations for publication of persian docs. --- docs/fa/changelog.md | 1 + docs/fa/data_types/array.md | 2 +- docs/fa/data_types/boolean.md | 4 +- docs/fa/data_types/date.md | 2 +- docs/fa/data_types/datetime.md | 2 +- docs/fa/data_types/enum.md | 4 +- docs/fa/data_types/fixedstring.md | 2 +- docs/fa/data_types/float.md | 10 +- docs/fa/data_types/index.md | 4 - docs/fa/data_types/int_uint.md | 2 +- .../aggregatefunction.md | 2 +- .../nested_data_structures/index.md | 2 +- .../nested_data_structures/nested.md | 8 +- .../special_data_types/expression.md | 2 +- .../fa/data_types/special_data_types/index.md | 2 +- docs/fa/data_types/special_data_types/set.md | 2 +- docs/fa/data_types/string.md | 2 +- docs/fa/data_types/tuple.md | 2 +- docs/fa/development/architecture.md | 1 + docs/fa/development/build.md | 1 + docs/fa/development/build_osx.md | 1 + docs/fa/development/index.md | 1 + docs/fa/development/style.md | 1 + docs/fa/development/tests.md | 1 + docs/fa/faq/general.md | 1 + .../example_datasets/amplab_benchmark.md | 8 +- .../example_datasets/criteo.md | 8 +- .../example_datasets/nyc_taxi.md | 24 +-- .../example_datasets/ontime.md | 38 ++-- .../example_datasets/star_schema.md | 8 +- .../example_datasets/wikistat.md | 4 +- docs/fa/getting_started/index.md | 22 +-- docs/fa/images/column_oriented.gif | Bin 0 -> 45485 bytes docs/fa/images/logo.svg | 12 ++ docs/fa/images/row_oriented.gif | Bin 0 -> 41571 bytes docs/fa/index.md | 2 +- docs/fa/interfaces/cli.md | 6 +- docs/fa/interfaces/formats.md | 30 +-- docs/fa/interfaces/http_interface.md | 38 ++-- docs/fa/interfaces/index.md | 2 +- docs/fa/interfaces/jdbc.md | 2 +- docs/fa/interfaces/tcp.md | 2 +- .../third-party_client_libraries.md | 2 +- docs/fa/interfaces/third-party_gui.md | 2 +- docs/fa/introduction/distinctive_features.md | 2 +- .../features_considered_disadvantages.md | 2 +- docs/fa/introduction/performance.md | 2 +- docs/fa/introduction/ya_metrika_task.md | 2 +- docs/fa/operations/access_rights.md | 1 + docs/fa/operations/configuration_files.md | 1 + docs/fa/operations/index.md | 1 + docs/fa/operations/quotas.md | 1 + docs/fa/operations/server_settings/index.md | 1 + .../fa/operations/server_settings/settings.md | 1 + docs/fa/operations/settings/index.md | 1 + .../operations/settings/query_complexity.md | 1 + docs/fa/operations/settings/settings.md | 1 + .../operations/settings/settings_profiles.md | 1 + docs/fa/operations/system_tables.md | 1 + .../table_engines/aggregatingmergetree.md | 1 + docs/fa/operations/table_engines/buffer.md | 1 + .../table_engines/collapsingmergetree.md | 1 + .../table_engines/custom_partitioning_key.md | 1 + .../fa/operations/table_engines/dictionary.md | 1 + .../operations/table_engines/distributed.md | 1 + .../operations/table_engines/external_data.md | 1 + docs/fa/operations/table_engines/file.md | 1 + .../table_engines/graphitemergetree.md | 1 + docs/fa/operations/table_engines/index.md | 1 + docs/fa/operations/table_engines/join.md | 1 + docs/fa/operations/table_engines/kafka.md | 1 + docs/fa/operations/table_engines/log.md | 1 + .../table_engines/materializedview.md | 1 + docs/fa/operations/table_engines/memory.md | 1 + docs/fa/operations/table_engines/merge.md | 1 + docs/fa/operations/table_engines/mergetree.md | 1 + docs/fa/operations/table_engines/mysql.md | 1 + docs/fa/operations/table_engines/null.md | 1 + .../table_engines/replacingmergetree.md | 1 + .../operations/table_engines/replication.md | 1 + docs/fa/operations/table_engines/set.md | 1 + .../table_engines/summingmergetree.md | 1 + docs/fa/operations/table_engines/tinylog.md | 1 + docs/fa/operations/table_engines/url.md | 1 + docs/fa/operations/table_engines/view.md | 1 + docs/fa/operations/tips.md | 1 + docs/fa/operations/utils/clickhouse-copier.md | 1 + docs/fa/operations/utils/clickhouse-local.md | 1 + docs/fa/operations/utils/index.md | 1 + .../agg_functions/combinators.md | 1 + docs/fa/query_language/agg_functions/index.md | 1 + .../agg_functions/parametric_functions.md | 1 + .../query_language/agg_functions/reference.md | 1 + docs/fa/query_language/alter.md | 1 + docs/fa/query_language/create.md | 1 + .../fa/query_language/dicts/external_dicts.md | 1 + .../dicts/external_dicts_dict.md | 1 + .../dicts/external_dicts_dict_layout.md | 1 + .../dicts/external_dicts_dict_lifetime.md | 1 + .../dicts/external_dicts_dict_sources.md | 1 + .../dicts/external_dicts_dict_structure.md | 1 + docs/fa/query_language/dicts/index.md | 1 + .../fa/query_language/dicts/internal_dicts.md | 1 + .../functions/arithmetic_functions.md | 1 + .../functions/array_functions.md | 1 + .../fa/query_language/functions/array_join.md | 1 + .../query_language/functions/bit_functions.md | 1 + .../functions/comparison_functions.md | 1 + .../functions/conditional_functions.md | 1 + .../functions/date_time_functions.md | 1 + .../functions/encoding_functions.md | 1 + .../functions/ext_dict_functions.md | 1 + .../functions/hash_functions.md | 1 + .../functions/higher_order_functions.md | 1 + .../query_language/functions/in_functions.md | 1 + docs/fa/query_language/functions/index.md | 1 + .../functions/ip_address_functions.md | 1 + .../functions/json_functions.md | 1 + .../functions/logical_functions.md | 1 + .../functions/math_functions.md | 1 + .../functions/other_functions.md | 1 + .../functions/random_functions.md | 1 + .../functions/rounding_functions.md | 1 + .../functions/splitting_merging_functions.md | 1 + .../functions/string_functions.md | 1 + .../functions/string_replace_functions.md | 1 + .../functions/string_search_functions.md | 1 + .../functions/type_conversion_functions.md | 1 + .../query_language/functions/url_functions.md | 1 + .../functions/ym_dict_functions.md | 1 + docs/fa/query_language/index.md | 1 + docs/fa/query_language/insert_into.md | 1 + docs/fa/query_language/misc.md | 1 + docs/fa/query_language/operators.md | 1 + docs/fa/query_language/select.md | 1 + docs/fa/query_language/syntax.md | 1 + .../fa/query_language/table_functions/file.md | 1 + .../query_language/table_functions/index.md | 1 + .../query_language/table_functions/merge.md | 1 + .../query_language/table_functions/numbers.md | 1 + .../query_language/table_functions/remote.md | 1 + docs/fa/query_language/table_functions/url.md | 1 + docs/fa/roadmap.md | 1 + docs/fa/security_changelog.md | 1 + docs/toc_fa.yml | 177 ++++++++++++++++++ docs/tools/build.py | 11 +- .../stylesheets/application.2a88008a.css | 2 + docs/tools/mkdocs-material-theme/base.html | 13 +- .../partials/language/fa.html | 22 +++ 149 files changed, 455 insertions(+), 146 deletions(-) create mode 120000 docs/fa/changelog.md create mode 120000 docs/fa/development/architecture.md create mode 120000 docs/fa/development/build.md create mode 120000 docs/fa/development/build_osx.md create mode 120000 docs/fa/development/index.md create mode 120000 docs/fa/development/style.md create mode 120000 docs/fa/development/tests.md create mode 120000 docs/fa/faq/general.md create mode 100644 docs/fa/images/column_oriented.gif create mode 100644 docs/fa/images/logo.svg create mode 100644 docs/fa/images/row_oriented.gif create mode 120000 docs/fa/operations/access_rights.md create mode 120000 docs/fa/operations/configuration_files.md create mode 120000 docs/fa/operations/index.md create mode 120000 docs/fa/operations/quotas.md create mode 120000 docs/fa/operations/server_settings/index.md create mode 120000 docs/fa/operations/server_settings/settings.md create mode 120000 docs/fa/operations/settings/index.md create mode 120000 docs/fa/operations/settings/query_complexity.md create mode 120000 docs/fa/operations/settings/settings.md create mode 120000 docs/fa/operations/settings/settings_profiles.md create mode 120000 docs/fa/operations/system_tables.md create mode 120000 docs/fa/operations/table_engines/aggregatingmergetree.md create mode 120000 docs/fa/operations/table_engines/buffer.md create mode 120000 docs/fa/operations/table_engines/collapsingmergetree.md create mode 120000 docs/fa/operations/table_engines/custom_partitioning_key.md create mode 120000 docs/fa/operations/table_engines/dictionary.md create mode 120000 docs/fa/operations/table_engines/distributed.md create mode 120000 docs/fa/operations/table_engines/external_data.md create mode 120000 docs/fa/operations/table_engines/file.md create mode 120000 docs/fa/operations/table_engines/graphitemergetree.md create mode 120000 docs/fa/operations/table_engines/index.md create mode 120000 docs/fa/operations/table_engines/join.md create mode 120000 docs/fa/operations/table_engines/kafka.md create mode 120000 docs/fa/operations/table_engines/log.md create mode 120000 docs/fa/operations/table_engines/materializedview.md create mode 120000 docs/fa/operations/table_engines/memory.md create mode 120000 docs/fa/operations/table_engines/merge.md create mode 120000 docs/fa/operations/table_engines/mergetree.md create mode 120000 docs/fa/operations/table_engines/mysql.md create mode 120000 docs/fa/operations/table_engines/null.md create mode 120000 docs/fa/operations/table_engines/replacingmergetree.md create mode 120000 docs/fa/operations/table_engines/replication.md create mode 120000 docs/fa/operations/table_engines/set.md create mode 120000 docs/fa/operations/table_engines/summingmergetree.md create mode 120000 docs/fa/operations/table_engines/tinylog.md create mode 120000 docs/fa/operations/table_engines/url.md create mode 120000 docs/fa/operations/table_engines/view.md create mode 120000 docs/fa/operations/tips.md create mode 120000 docs/fa/operations/utils/clickhouse-copier.md create mode 120000 docs/fa/operations/utils/clickhouse-local.md create mode 120000 docs/fa/operations/utils/index.md create mode 120000 docs/fa/query_language/agg_functions/combinators.md create mode 120000 docs/fa/query_language/agg_functions/index.md create mode 120000 docs/fa/query_language/agg_functions/parametric_functions.md create mode 120000 docs/fa/query_language/agg_functions/reference.md create mode 120000 docs/fa/query_language/alter.md create mode 120000 docs/fa/query_language/create.md create mode 120000 docs/fa/query_language/dicts/external_dicts.md create mode 120000 docs/fa/query_language/dicts/external_dicts_dict.md create mode 120000 docs/fa/query_language/dicts/external_dicts_dict_layout.md create mode 120000 docs/fa/query_language/dicts/external_dicts_dict_lifetime.md create mode 120000 docs/fa/query_language/dicts/external_dicts_dict_sources.md create mode 120000 docs/fa/query_language/dicts/external_dicts_dict_structure.md create mode 120000 docs/fa/query_language/dicts/index.md create mode 120000 docs/fa/query_language/dicts/internal_dicts.md create mode 120000 docs/fa/query_language/functions/arithmetic_functions.md create mode 120000 docs/fa/query_language/functions/array_functions.md create mode 120000 docs/fa/query_language/functions/array_join.md create mode 120000 docs/fa/query_language/functions/bit_functions.md create mode 120000 docs/fa/query_language/functions/comparison_functions.md create mode 120000 docs/fa/query_language/functions/conditional_functions.md create mode 120000 docs/fa/query_language/functions/date_time_functions.md create mode 120000 docs/fa/query_language/functions/encoding_functions.md create mode 120000 docs/fa/query_language/functions/ext_dict_functions.md create mode 120000 docs/fa/query_language/functions/hash_functions.md create mode 120000 docs/fa/query_language/functions/higher_order_functions.md create mode 120000 docs/fa/query_language/functions/in_functions.md create mode 120000 docs/fa/query_language/functions/index.md create mode 120000 docs/fa/query_language/functions/ip_address_functions.md create mode 120000 docs/fa/query_language/functions/json_functions.md create mode 120000 docs/fa/query_language/functions/logical_functions.md create mode 120000 docs/fa/query_language/functions/math_functions.md create mode 120000 docs/fa/query_language/functions/other_functions.md create mode 120000 docs/fa/query_language/functions/random_functions.md create mode 120000 docs/fa/query_language/functions/rounding_functions.md create mode 120000 docs/fa/query_language/functions/splitting_merging_functions.md create mode 120000 docs/fa/query_language/functions/string_functions.md create mode 120000 docs/fa/query_language/functions/string_replace_functions.md create mode 120000 docs/fa/query_language/functions/string_search_functions.md create mode 120000 docs/fa/query_language/functions/type_conversion_functions.md create mode 120000 docs/fa/query_language/functions/url_functions.md create mode 120000 docs/fa/query_language/functions/ym_dict_functions.md create mode 120000 docs/fa/query_language/index.md create mode 120000 docs/fa/query_language/insert_into.md create mode 120000 docs/fa/query_language/misc.md create mode 120000 docs/fa/query_language/operators.md create mode 120000 docs/fa/query_language/select.md create mode 120000 docs/fa/query_language/syntax.md create mode 120000 docs/fa/query_language/table_functions/file.md create mode 120000 docs/fa/query_language/table_functions/index.md create mode 120000 docs/fa/query_language/table_functions/merge.md create mode 120000 docs/fa/query_language/table_functions/numbers.md create mode 120000 docs/fa/query_language/table_functions/remote.md create mode 120000 docs/fa/query_language/table_functions/url.md create mode 120000 docs/fa/roadmap.md create mode 120000 docs/fa/security_changelog.md create mode 100644 docs/toc_fa.yml create mode 100644 docs/tools/mkdocs-material-theme/assets/stylesheets/application.2a88008a.css create mode 100644 docs/tools/mkdocs-material-theme/partials/language/fa.html diff --git a/docs/fa/changelog.md b/docs/fa/changelog.md new file mode 120000 index 00000000000..b84693cec46 --- /dev/null +++ b/docs/fa/changelog.md @@ -0,0 +1 @@ +../../CHANGELOG_RU.md \ No newline at end of file diff --git a/docs/fa/data_types/array.md b/docs/fa/data_types/array.md index 81cf0d6d94a..9b7c70618a3 100644 --- a/docs/fa/data_types/array.md +++ b/docs/fa/data_types/array.md @@ -1,4 +1,4 @@ -
+
# Array(T) diff --git a/docs/fa/data_types/boolean.md b/docs/fa/data_types/boolean.md index 697b8e87eda..7fe6763ecc7 100644 --- a/docs/fa/data_types/boolean.md +++ b/docs/fa/data_types/boolean.md @@ -1,7 +1,7 @@ -
+
# مقادیر Boolean type مخصوص مقادیر boolean وجود ندارد. از Uint8 و محدود شده به 0 و 1 می توان استفاده کرد. -
\ No newline at end of file +
diff --git a/docs/fa/data_types/date.md b/docs/fa/data_types/date.md index 64f0d6d7691..3b77e5ce0c5 100644 --- a/docs/fa/data_types/date.md +++ b/docs/fa/data_types/date.md @@ -1,4 +1,4 @@ -
+
# Date diff --git a/docs/fa/data_types/datetime.md b/docs/fa/data_types/datetime.md index e278c95c8b7..6731b1bea6c 100644 --- a/docs/fa/data_types/datetime.md +++ b/docs/fa/data_types/datetime.md @@ -1,4 +1,4 @@ -
+
# DateTime diff --git a/docs/fa/data_types/enum.md b/docs/fa/data_types/enum.md index f340cd14f6d..bf678647e8e 100644 --- a/docs/fa/data_types/enum.md +++ b/docs/fa/data_types/enum.md @@ -1,4 +1,4 @@ -
+
# Enum @@ -12,7 +12,7 @@ Enum8 یا Enum16، به شما اجازه ی ذخیره سازی مجموعه Enum8('hello' = 1, 'world' = 2) ``` -
+
- مقدار داخل این ستون می تواند یکی از دو مقدار 'hello' یا 'world' باشد. diff --git a/docs/fa/data_types/fixedstring.md b/docs/fa/data_types/fixedstring.md index e1aa5cada4e..f337fe0ef46 100644 --- a/docs/fa/data_types/fixedstring.md +++ b/docs/fa/data_types/fixedstring.md @@ -1,4 +1,4 @@ -
+
# FixedString(N) diff --git a/docs/fa/data_types/float.md b/docs/fa/data_types/float.md index 63e43580f02..60fb9fb380a 100644 --- a/docs/fa/data_types/float.md +++ b/docs/fa/data_types/float.md @@ -1,4 +1,4 @@ -
+
# Float32, Float64 @@ -26,7 +26,7 @@ SELECT 1 - 0.9 └─────────────────────┘ ``` -
+
- نتایج محاسبات بسته به متد محاسباتی می باشد (نوع processor و معماری سیستم). - محاسبات Float ممکن اسن نتایجی مثل infinity (`inf`) و "Not-a-number" (`Nan`) داشته باشد. این در هنگام پردازش نتایج محاسبات باید مورد توجه قرار گیرد. @@ -50,7 +50,7 @@ SELECT 0.5 / 0 └────────────────┘ ``` -
+
- `-Inf` – Negative infinity. @@ -66,7 +66,7 @@ SELECT -0.5 / 0 └─────────────────┘ ``` -
+
- `NaN` – Not a number. @@ -82,7 +82,7 @@ SELECT 0 / 0 └──────────────┘ ``` -
+
قوانین مربوط به مرتب سازی ` Nan ` را در بخش [ORDER BY clause](../query_language/select.md#query_language-queries-order_by) ببینید. diff --git a/docs/fa/data_types/index.md b/docs/fa/data_types/index.md index e4b70fd5715..0f3e5f17609 100644 --- a/docs/fa/data_types/index.md +++ b/docs/fa/data_types/index.md @@ -1,11 +1,7 @@ -
- # Data types ClickHouse قابلیت ذخیره سازی انواع type های مختلف برای ذخیره داده ها در جداول را دارا می باشد. این بخش انواع data type های قابل پشتیبانی در ClickHouse را شرح می دهد، همچنین ملاحطات آنها در هنگام استفاده آنها را شرح می دهد. - -
\ No newline at end of file diff --git a/docs/fa/data_types/int_uint.md b/docs/fa/data_types/int_uint.md index 89d9aaf42f6..fee85d72f33 100644 --- a/docs/fa/data_types/int_uint.md +++ b/docs/fa/data_types/int_uint.md @@ -1,4 +1,4 @@ -
+
# UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64 diff --git a/docs/fa/data_types/nested_data_structures/aggregatefunction.md b/docs/fa/data_types/nested_data_structures/aggregatefunction.md index a6906423875..8d56156390e 100644 --- a/docs/fa/data_types/nested_data_structures/aggregatefunction.md +++ b/docs/fa/data_types/nested_data_structures/aggregatefunction.md @@ -1,4 +1,4 @@ -
+
# AggregateFunction(name, types_of_arguments...) diff --git a/docs/fa/data_types/nested_data_structures/index.md b/docs/fa/data_types/nested_data_structures/index.md index 9b5a4ed4365..def4991e5bc 100644 --- a/docs/fa/data_types/nested_data_structures/index.md +++ b/docs/fa/data_types/nested_data_structures/index.md @@ -1,4 +1,4 @@ -
+
# Nested data structures diff --git a/docs/fa/data_types/nested_data_structures/nested.md b/docs/fa/data_types/nested_data_structures/nested.md index 690c44f691d..a02aec74872 100644 --- a/docs/fa/data_types/nested_data_structures/nested.md +++ b/docs/fa/data_types/nested_data_structures/nested.md @@ -1,4 +1,4 @@ -
+
# Nested(Name1 Type1, Name2 Type2, ...) @@ -31,7 +31,7 @@ CREATE TABLE test.visits ) ENGINE = CollapsingMergeTree(StartDate, intHash32(UserID), (CounterID, StartDate, intHash32(UserID), VisitID), 8192, Sign) ``` -
+
این مثال `Goals` را به عنوان یک ساختار داده nested تعریف می کند، که می تواند شامل داده های مربوط به conversion (اهداف رسیده) باشد. هر سطر در جدول `visit` می تواند با صفر یا چند coversion ارتباط داشته باشد. @@ -67,7 +67,7 @@ LIMIT 10 └────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────────┘ ``` -
+
ساده ترین راه برای فکر کردن به یک ساختار داده nestet این است که، یک nestet مجموعه ای از آرایه های چند ستونی با طول ثابت است. @@ -100,7 +100,7 @@ LIMIT 10 └─────────┴─────────────────────┘ ``` -
+
شما نمیتوانید در قسمت SELECT تمام ساختار داده ی nested را قرار دهید. شما فقط می توانید ستون های فردی که هر کدام بخشی از این ساختار داده هستند را لیست کنید. diff --git a/docs/fa/data_types/special_data_types/expression.md b/docs/fa/data_types/special_data_types/expression.md index 8272494a822..69ac90a3976 100644 --- a/docs/fa/data_types/special_data_types/expression.md +++ b/docs/fa/data_types/special_data_types/expression.md @@ -1,4 +1,4 @@ -
+
# Expression diff --git a/docs/fa/data_types/special_data_types/index.md b/docs/fa/data_types/special_data_types/index.md index e2781a31040..88f32012867 100644 --- a/docs/fa/data_types/special_data_types/index.md +++ b/docs/fa/data_types/special_data_types/index.md @@ -1,4 +1,4 @@ -
+
# Special data types diff --git a/docs/fa/data_types/special_data_types/set.md b/docs/fa/data_types/special_data_types/set.md index dcc0df11955..e518997271b 100644 --- a/docs/fa/data_types/special_data_types/set.md +++ b/docs/fa/data_types/special_data_types/set.md @@ -1,4 +1,4 @@ -
+
# Set diff --git a/docs/fa/data_types/string.md b/docs/fa/data_types/string.md index b98c6456f31..51734007388 100644 --- a/docs/fa/data_types/string.md +++ b/docs/fa/data_types/string.md @@ -1,4 +1,4 @@ -
+
# String diff --git a/docs/fa/data_types/tuple.md b/docs/fa/data_types/tuple.md index 1adfc5b8244..1acb21fb2fd 100644 --- a/docs/fa/data_types/tuple.md +++ b/docs/fa/data_types/tuple.md @@ -1,4 +1,4 @@ -
+
# Tuple(T1, T2, ...) diff --git a/docs/fa/development/architecture.md b/docs/fa/development/architecture.md new file mode 120000 index 00000000000..abda4dd48a8 --- /dev/null +++ b/docs/fa/development/architecture.md @@ -0,0 +1 @@ +../../en/development/architecture.md \ No newline at end of file diff --git a/docs/fa/development/build.md b/docs/fa/development/build.md new file mode 120000 index 00000000000..480dbc2e9f5 --- /dev/null +++ b/docs/fa/development/build.md @@ -0,0 +1 @@ +../../en/development/build.md \ No newline at end of file diff --git a/docs/fa/development/build_osx.md b/docs/fa/development/build_osx.md new file mode 120000 index 00000000000..f9adaf24584 --- /dev/null +++ b/docs/fa/development/build_osx.md @@ -0,0 +1 @@ +../../en/development/build_osx.md \ No newline at end of file diff --git a/docs/fa/development/index.md b/docs/fa/development/index.md new file mode 120000 index 00000000000..1e2ad97dcc5 --- /dev/null +++ b/docs/fa/development/index.md @@ -0,0 +1 @@ +../../en/development/index.md \ No newline at end of file diff --git a/docs/fa/development/style.md b/docs/fa/development/style.md new file mode 120000 index 00000000000..c1bbf11f421 --- /dev/null +++ b/docs/fa/development/style.md @@ -0,0 +1 @@ +../../en/development/style.md \ No newline at end of file diff --git a/docs/fa/development/tests.md b/docs/fa/development/tests.md new file mode 120000 index 00000000000..c03d36c3916 --- /dev/null +++ b/docs/fa/development/tests.md @@ -0,0 +1 @@ +../../en/development/tests.md \ No newline at end of file diff --git a/docs/fa/faq/general.md b/docs/fa/faq/general.md new file mode 120000 index 00000000000..bc267395b1b --- /dev/null +++ b/docs/fa/faq/general.md @@ -0,0 +1 @@ +../../en/faq/general.md \ No newline at end of file diff --git a/docs/fa/getting_started/example_datasets/amplab_benchmark.md b/docs/fa/getting_started/example_datasets/amplab_benchmark.md index 3cb8f1a17cf..acdf6dc8704 100644 --- a/docs/fa/getting_started/example_datasets/amplab_benchmark.md +++ b/docs/fa/getting_started/example_datasets/amplab_benchmark.md @@ -1,4 +1,4 @@ -
+
# بنچمارک AMPLab Big Data @@ -23,7 +23,7 @@ s3cmd sync s3://big-data-benchmark/pavlo/text-deflate/5nodes/ . cd .. ``` -
+
این query های ClickHouse را اجرا کنید: @@ -91,7 +91,7 @@ CREATE TABLE uservisits_5nodes_on_single ) ENGINE = MergeTree(visitDate, visitDate, 8192); ``` -
+
به کنسول برگردید و دستورات زیر را مجددا اجرا کنید: @@ -106,7 +106,7 @@ for i in 5nodes/rankings/*.deflate; do echo $i; zlib-flate -uncompress < $i | cl for i in 5nodes/uservisits/*.deflate; do echo $i; zlib-flate -uncompress < $i | clickhouse-client --host=example-perftest01j --query="INSERT INTO uservisits_5nodes_on_single FORMAT CSV"; done ``` -
+
query های گرفتن data sample diff --git a/docs/fa/getting_started/example_datasets/criteo.md b/docs/fa/getting_started/example_datasets/criteo.md index 6ac6a4f5414..57f4007efa9 100644 --- a/docs/fa/getting_started/example_datasets/criteo.md +++ b/docs/fa/getting_started/example_datasets/criteo.md @@ -1,4 +1,4 @@ -
+
# ترابایت از لاگ های کلیک از سرویس Criteo @@ -12,7 +12,7 @@ CREATE TABLE criteo_log (date Date, clicked UInt8, int1 Int32, int2 Int32, int3 Int32, int4 Int32, int5 Int32, int6 Int32, int7 Int32, int8 Int32, int9 Int32, int10 Int32, int11 Int32, int12 Int32, int13 Int32, cat1 String, cat2 String, cat3 String, cat4 String, cat5 String, cat6 String, cat7 String, cat8 String, cat9 String, cat10 String, cat11 String, cat12 String, cat13 String, cat14 String, cat15 String, cat16 String, cat17 String, cat18 String, cat19 String, cat20 String, cat21 String, cat22 String, cat23 String, cat24 String, cat25 String, cat26 String) ENGINE = Log ``` -
+
داده ها را دانلود کنید: @@ -22,7 +22,7 @@ CREATE TABLE criteo_log (date Date, clicked UInt8, int1 Int32, int2 Int32, int3 for i in {00..23}; do echo $i; zcat datasets/criteo/day_${i#0}.gz | sed -r 's/^/2000-01-'${i/00/24}'\t/' | clickhouse-client --host=example-perftest01j --query="INSERT INTO criteo_log FORMAT TabSeparated"; done ``` -
+
یک جدول برای داده های تبدیل شده ایجاد کنید: @@ -75,7 +75,7 @@ CREATE TABLE criteo ) ENGINE = MergeTree(date, intHash32(icat1), (date, intHash32(icat1)), 8192) ``` -
+
داده ها را از لاگ raw انتقال و به جدول دوم وارد کنید: diff --git a/docs/fa/getting_started/example_datasets/nyc_taxi.md b/docs/fa/getting_started/example_datasets/nyc_taxi.md index 72a6b872d0d..bf478583304 100644 --- a/docs/fa/getting_started/example_datasets/nyc_taxi.md +++ b/docs/fa/getting_started/example_datasets/nyc_taxi.md @@ -1,4 +1,4 @@ -
+
# داده های تاکسی New York @@ -21,7 +21,7 @@ mv data/yellow_tripdata_2010-02.csv_ data/yellow_tripdata_2010-02.csv mv data/yellow_tripdata_2010-03.csv_ data/yellow_tripdata_2010-03.csv ``` -
+
سپس داده ها باید در PostgreSQL پیش پردازش شوند. این کار نقاط انتخابی چند ضلعی را ایجاد می کند (برای مطابقت با نقاط بر روی نقشه با مناطق شهر نیویورک) و تمام داده ها را با استفاده از JOIN در یک جدول flat و denormal ترکیب می کند. برای این کار شما نیاز به نصب PostgreSQL با پشتیبانی از PostGIS دارید. @@ -42,7 +42,7 @@ time psql nyc-taxi-data -c "SELECT count(*) FROM trips;" real 7m9.164s ``` -
+
(در یکی از پست های مقالات Mark Litwintschik این کمی بیشتر از 1.1 میلیارد سطر گزارش شده است.) @@ -122,7 +122,7 @@ COPY ) TO '/opt/milovidov/nyc-taxi-data/trips.tsv'; ``` -
+
snapshot از داده ها با سرعت 50 مگابایت در ثانیه انجام می شود. در هنگام ایجاد snapshot، PostgreSQL داده ها را با سرعت 28 مگابایت در ثانیه از روی می خواند. این کار حدود 5 ساعت زمان میبرد. نتیجه کار فایل TSV با حجم 590612904969 بایت می باشد. @@ -187,7 +187,7 @@ dropoff_puma Nullable(String) ) ENGINE = Log; ``` -
+
برای تبدیل فیلد ها به data type های صحیح تر و در صورت امکان، حذف NULL ها لازم است. @@ -199,7 +199,7 @@ time clickhouse-client --query="INSERT INTO trips FORMAT TabSeparated" < trips.t real 75m56.214s ``` -
+
داده ها با سرعت 112 تا 140 مگابیت در ثانیه خوانده می شوند. load کردن داده ها در جدول Log Type در یک Stream، 76 دقیقه زمان کشید. این داده ها در این جدول 142 گیگابایت فضا اشغال می کنند. @@ -275,7 +275,7 @@ toUInt16(ifNull(dropoff_puma, '0')) AS dropoff_puma FROM trips ``` -
+
این کار با سرعت 428 هزار رکورد در ثانیه و 3030 ثانیه طول خواهد کشید. برای load سریعتر، شما می توانید یک جدول با موتور `Log` به جای `MergeTree` بسازید. در این مورد، دانلود سریعتر از 200 ثانیه کار می کند. @@ -295,7 +295,7 @@ WHERE (table = 'trips_mergetree') AND active └────────────────────────────────┘ ``` -
+
در میان چیزهای دیگر، شما می تونید از دستور OPTIMIZE بر روی MergeTree استفاده کنید. اما از آنجایی که بدون این دستور همه چیز خوب است، اجرای این دستور ضروری نیست.. @@ -338,7 +338,7 @@ ORDER BY year, count(*) DESC 3.593 seconds. -
+
کانفیگ سرور به این صورت بود: @@ -358,7 +358,7 @@ Two Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz, 16 physical kernels total, CREATE TABLE default.trips_mergetree_third ( trip_id UInt32, vendor_id Enum8('1' = 1, '2' = 2, 'CMT' = 3, 'VTS' = 4, 'DDS' = 5, 'B02512' = 10, 'B02598' = 11, 'B02617' = 12, 'B02682' = 13, 'B02764' = 14), pickup_date Date, pickup_datetime DateTime, dropoff_date Date, dropoff_datetime DateTime, store_and_fwd_flag UInt8, rate_code_id UInt8, pickup_longitude Float64, pickup_latitude Float64, dropoff_longitude Float64, dropoff_latitude Float64, passenger_count UInt8, trip_distance Float64, fare_amount Float32, extra Float32, mta_tax Float32, tip_amount Float32, tolls_amount Float32, ehail_fee Float32, improvement_surcharge Float32, total_amount Float32, payment_type_ Enum8('UNK' = 0, 'CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4), trip_type UInt8, pickup FixedString(25), dropoff FixedString(25), cab_type Enum8('yellow' = 1, 'green' = 2, 'uber' = 3), pickup_nyct2010_gid UInt8, pickup_ctlabel Float32, pickup_borocode UInt8, pickup_boroname Enum8('' = 0, 'Manhattan' = 1, 'Bronx' = 2, 'Brooklyn' = 3, 'Queens' = 4, 'Staten Island' = 5), pickup_ct2010 FixedString(6), pickup_boroct2010 FixedString(7), pickup_cdeligibil Enum8(' ' = 0, 'E' = 1, 'I' = 2), pickup_ntacode FixedString(4), pickup_ntaname Enum16('' = 0, 'Airport' = 1, 'Allerton-Pelham Gardens' = 2, 'Annadale-Huguenot-Prince\'s Bay-Eltingville' = 3, 'Arden Heights' = 4, 'Astoria' = 5, 'Auburndale' = 6, 'Baisley Park' = 7, 'Bath Beach' = 8, 'Battery Park City-Lower Manhattan' = 9, 'Bay Ridge' = 10, 'Bayside-Bayside Hills' = 11, 'Bedford' = 12, 'Bedford Park-Fordham North' = 13, 'Bellerose' = 14, 'Belmont' = 15, 'Bensonhurst East' = 16, 'Bensonhurst West' = 17, 'Borough Park' = 18, 'Breezy Point-Belle Harbor-Rockaway Park-Broad Channel' = 19, 'Briarwood-Jamaica Hills' = 20, 'Brighton Beach' = 21, 'Bronxdale' = 22, 'Brooklyn Heights-Cobble Hill' = 23, 'Brownsville' = 24, 'Bushwick North' = 25, 'Bushwick South' = 26, 'Cambria Heights' = 27, 'Canarsie' = 28, 'Carroll Gardens-Columbia Street-Red Hook' = 29, 'Central Harlem North-Polo Grounds' = 30, 'Central Harlem South' = 31, 'Charleston-Richmond Valley-Tottenville' = 32, 'Chinatown' = 33, 'Claremont-Bathgate' = 34, 'Clinton' = 35, 'Clinton Hill' = 36, 'Co-op City' = 37, 'College Point' = 38, 'Corona' = 39, 'Crotona Park East' = 40, 'Crown Heights North' = 41, 'Crown Heights South' = 42, 'Cypress Hills-City Line' = 43, 'DUMBO-Vinegar Hill-Downtown Brooklyn-Boerum Hill' = 44, 'Douglas Manor-Douglaston-Little Neck' = 45, 'Dyker Heights' = 46, 'East Concourse-Concourse Village' = 47, 'East Elmhurst' = 48, 'East Flatbush-Farragut' = 49, 'East Flushing' = 50, 'East Harlem North' = 51, 'East Harlem South' = 52, 'East New York' = 53, 'East New York (Pennsylvania Ave)' = 54, 'East Tremont' = 55, 'East Village' = 56, 'East Williamsburg' = 57, 'Eastchester-Edenwald-Baychester' = 58, 'Elmhurst' = 59, 'Elmhurst-Maspeth' = 60, 'Erasmus' = 61, 'Far Rockaway-Bayswater' = 62, 'Flatbush' = 63, 'Flatlands' = 64, 'Flushing' = 65, 'Fordham South' = 66, 'Forest Hills' = 67, 'Fort Greene' = 68, 'Fresh Meadows-Utopia' = 69, 'Ft. Totten-Bay Terrace-Clearview' = 70, 'Georgetown-Marine Park-Bergen Beach-Mill Basin' = 71, 'Glen Oaks-Floral Park-New Hyde Park' = 72, 'Glendale' = 73, 'Gramercy' = 74, 'Grasmere-Arrochar-Ft. Wadsworth' = 75, 'Gravesend' = 76, 'Great Kills' = 77, 'Greenpoint' = 78, 'Grymes Hill-Clifton-Fox Hills' = 79, 'Hamilton Heights' = 80, 'Hammels-Arverne-Edgemere' = 81, 'Highbridge' = 82, 'Hollis' = 83, 'Homecrest' = 84, 'Hudson Yards-Chelsea-Flatiron-Union Square' = 85, 'Hunters Point-Sunnyside-West Maspeth' = 86, 'Hunts Point' = 87, 'Jackson Heights' = 88, 'Jamaica' = 89, 'Jamaica Estates-Holliswood' = 90, 'Kensington-Ocean Parkway' = 91, 'Kew Gardens' = 92, 'Kew Gardens Hills' = 93, 'Kingsbridge Heights' = 94, 'Laurelton' = 95, 'Lenox Hill-Roosevelt Island' = 96, 'Lincoln Square' = 97, 'Lindenwood-Howard Beach' = 98, 'Longwood' = 99, 'Lower East Side' = 100, 'Madison' = 101, 'Manhattanville' = 102, 'Marble Hill-Inwood' = 103, 'Mariner\'s Harbor-Arlington-Port Ivory-Graniteville' = 104, 'Maspeth' = 105, 'Melrose South-Mott Haven North' = 106, 'Middle Village' = 107, 'Midtown-Midtown South' = 108, 'Midwood' = 109, 'Morningside Heights' = 110, 'Morrisania-Melrose' = 111, 'Mott Haven-Port Morris' = 112, 'Mount Hope' = 113, 'Murray Hill' = 114, 'Murray Hill-Kips Bay' = 115, 'New Brighton-Silver Lake' = 116, 'New Dorp-Midland Beach' = 117, 'New Springville-Bloomfield-Travis' = 118, 'North Corona' = 119, 'North Riverdale-Fieldston-Riverdale' = 120, 'North Side-South Side' = 121, 'Norwood' = 122, 'Oakland Gardens' = 123, 'Oakwood-Oakwood Beach' = 124, 'Ocean Hill' = 125, 'Ocean Parkway South' = 126, 'Old Astoria' = 127, 'Old Town-Dongan Hills-South Beach' = 128, 'Ozone Park' = 129, 'Park Slope-Gowanus' = 130, 'Parkchester' = 131, 'Pelham Bay-Country Club-City Island' = 132, 'Pelham Parkway' = 133, 'Pomonok-Flushing Heights-Hillcrest' = 134, 'Port Richmond' = 135, 'Prospect Heights' = 136, 'Prospect Lefferts Gardens-Wingate' = 137, 'Queens Village' = 138, 'Queensboro Hill' = 139, 'Queensbridge-Ravenswood-Long Island City' = 140, 'Rego Park' = 141, 'Richmond Hill' = 142, 'Ridgewood' = 143, 'Rikers Island' = 144, 'Rosedale' = 145, 'Rossville-Woodrow' = 146, 'Rugby-Remsen Village' = 147, 'Schuylerville-Throgs Neck-Edgewater Park' = 148, 'Seagate-Coney Island' = 149, 'Sheepshead Bay-Gerritsen Beach-Manhattan Beach' = 150, 'SoHo-TriBeCa-Civic Center-Little Italy' = 151, 'Soundview-Bruckner' = 152, 'Soundview-Castle Hill-Clason Point-Harding Park' = 153, 'South Jamaica' = 154, 'South Ozone Park' = 155, 'Springfield Gardens North' = 156, 'Springfield Gardens South-Brookville' = 157, 'Spuyten Duyvil-Kingsbridge' = 158, 'St. Albans' = 159, 'Stapleton-Rosebank' = 160, 'Starrett City' = 161, 'Steinway' = 162, 'Stuyvesant Heights' = 163, 'Stuyvesant Town-Cooper Village' = 164, 'Sunset Park East' = 165, 'Sunset Park West' = 166, 'Todt Hill-Emerson Hill-Heartland Village-Lighthouse Hill' = 167, 'Turtle Bay-East Midtown' = 168, 'University Heights-Morris Heights' = 169, 'Upper East Side-Carnegie Hill' = 170, 'Upper West Side' = 171, 'Van Cortlandt Village' = 172, 'Van Nest-Morris Park-Westchester Square' = 173, 'Washington Heights North' = 174, 'Washington Heights South' = 175, 'West Brighton' = 176, 'West Concourse' = 177, 'West Farms-Bronx River' = 178, 'West New Brighton-New Brighton-St. George' = 179, 'West Village' = 180, 'Westchester-Unionport' = 181, 'Westerleigh' = 182, 'Whitestone' = 183, 'Williamsbridge-Olinville' = 184, 'Williamsburg' = 185, 'Windsor Terrace' = 186, 'Woodhaven' = 187, 'Woodlawn-Wakefield' = 188, 'Woodside' = 189, 'Yorkville' = 190, 'park-cemetery-etc-Bronx' = 191, 'park-cemetery-etc-Brooklyn' = 192, 'park-cemetery-etc-Manhattan' = 193, 'park-cemetery-etc-Queens' = 194, 'park-cemetery-etc-Staten Island' = 195), pickup_puma UInt16, dropoff_nyct2010_gid UInt8, dropoff_ctlabel Float32, dropoff_borocode UInt8, dropoff_boroname Enum8('' = 0, 'Manhattan' = 1, 'Bronx' = 2, 'Brooklyn' = 3, 'Queens' = 4, 'Staten Island' = 5), dropoff_ct2010 FixedString(6), dropoff_boroct2010 FixedString(7), dropoff_cdeligibil Enum8(' ' = 0, 'E' = 1, 'I' = 2), dropoff_ntacode FixedString(4), dropoff_ntaname Enum16('' = 0, 'Airport' = 1, 'Allerton-Pelham Gardens' = 2, 'Annadale-Huguenot-Prince\'s Bay-Eltingville' = 3, 'Arden Heights' = 4, 'Astoria' = 5, 'Auburndale' = 6, 'Baisley Park' = 7, 'Bath Beach' = 8, 'Battery Park City-Lower Manhattan' = 9, 'Bay Ridge' = 10, 'Bayside-Bayside Hills' = 11, 'Bedford' = 12, 'Bedford Park-Fordham North' = 13, 'Bellerose' = 14, 'Belmont' = 15, 'Bensonhurst East' = 16, 'Bensonhurst West' = 17, 'Borough Park' = 18, 'Breezy Point-Belle Harbor-Rockaway Park-Broad Channel' = 19, 'Briarwood-Jamaica Hills' = 20, 'Brighton Beach' = 21, 'Bronxdale' = 22, 'Brooklyn Heights-Cobble Hill' = 23, 'Brownsville' = 24, 'Bushwick North' = 25, 'Bushwick South' = 26, 'Cambria Heights' = 27, 'Canarsie' = 28, 'Carroll Gardens-Columbia Street-Red Hook' = 29, 'Central Harlem North-Polo Grounds' = 30, 'Central Harlem South' = 31, 'Charleston-Richmond Valley-Tottenville' = 32, 'Chinatown' = 33, 'Claremont-Bathgate' = 34, 'Clinton' = 35, 'Clinton Hill' = 36, 'Co-op City' = 37, 'College Point' = 38, 'Corona' = 39, 'Crotona Park East' = 40, 'Crown Heights North' = 41, 'Crown Heights South' = 42, 'Cypress Hills-City Line' = 43, 'DUMBO-Vinegar Hill-Downtown Brooklyn-Boerum Hill' = 44, 'Douglas Manor-Douglaston-Little Neck' = 45, 'Dyker Heights' = 46, 'East Concourse-Concourse Village' = 47, 'East Elmhurst' = 48, 'East Flatbush-Farragut' = 49, 'East Flushing' = 50, 'East Harlem North' = 51, 'East Harlem South' = 52, 'East New York' = 53, 'East New York (Pennsylvania Ave)' = 54, 'East Tremont' = 55, 'East Village' = 56, 'East Williamsburg' = 57, 'Eastchester-Edenwald-Baychester' = 58, 'Elmhurst' = 59, 'Elmhurst-Maspeth' = 60, 'Erasmus' = 61, 'Far Rockaway-Bayswater' = 62, 'Flatbush' = 63, 'Flatlands' = 64, 'Flushing' = 65, 'Fordham South' = 66, 'Forest Hills' = 67, 'Fort Greene' = 68, 'Fresh Meadows-Utopia' = 69, 'Ft. Totten-Bay Terrace-Clearview' = 70, 'Georgetown-Marine Park-Bergen Beach-Mill Basin' = 71, 'Glen Oaks-Floral Park-New Hyde Park' = 72, 'Glendale' = 73, 'Gramercy' = 74, 'Grasmere-Arrochar-Ft. Wadsworth' = 75, 'Gravesend' = 76, 'Great Kills' = 77, 'Greenpoint' = 78, 'Grymes Hill-Clifton-Fox Hills' = 79, 'Hamilton Heights' = 80, 'Hammels-Arverne-Edgemere' = 81, 'Highbridge' = 82, 'Hollis' = 83, 'Homecrest' = 84, 'Hudson Yards-Chelsea-Flatiron-Union Square' = 85, 'Hunters Point-Sunnyside-West Maspeth' = 86, 'Hunts Point' = 87, 'Jackson Heights' = 88, 'Jamaica' = 89, 'Jamaica Estates-Holliswood' = 90, 'Kensington-Ocean Parkway' = 91, 'Kew Gardens' = 92, 'Kew Gardens Hills' = 93, 'Kingsbridge Heights' = 94, 'Laurelton' = 95, 'Lenox Hill-Roosevelt Island' = 96, 'Lincoln Square' = 97, 'Lindenwood-Howard Beach' = 98, 'Longwood' = 99, 'Lower East Side' = 100, 'Madison' = 101, 'Manhattanville' = 102, 'Marble Hill-Inwood' = 103, 'Mariner\'s Harbor-Arlington-Port Ivory-Graniteville' = 104, 'Maspeth' = 105, 'Melrose South-Mott Haven North' = 106, 'Middle Village' = 107, 'Midtown-Midtown South' = 108, 'Midwood' = 109, 'Morningside Heights' = 110, 'Morrisania-Melrose' = 111, 'Mott Haven-Port Morris' = 112, 'Mount Hope' = 113, 'Murray Hill' = 114, 'Murray Hill-Kips Bay' = 115, 'New Brighton-Silver Lake' = 116, 'New Dorp-Midland Beach' = 117, 'New Springville-Bloomfield-Travis' = 118, 'North Corona' = 119, 'North Riverdale-Fieldston-Riverdale' = 120, 'North Side-South Side' = 121, 'Norwood' = 122, 'Oakland Gardens' = 123, 'Oakwood-Oakwood Beach' = 124, 'Ocean Hill' = 125, 'Ocean Parkway South' = 126, 'Old Astoria' = 127, 'Old Town-Dongan Hills-South Beach' = 128, 'Ozone Park' = 129, 'Park Slope-Gowanus' = 130, 'Parkchester' = 131, 'Pelham Bay-Country Club-City Island' = 132, 'Pelham Parkway' = 133, 'Pomonok-Flushing Heights-Hillcrest' = 134, 'Port Richmond' = 135, 'Prospect Heights' = 136, 'Prospect Lefferts Gardens-Wingate' = 137, 'Queens Village' = 138, 'Queensboro Hill' = 139, 'Queensbridge-Ravenswood-Long Island City' = 140, 'Rego Park' = 141, 'Richmond Hill' = 142, 'Ridgewood' = 143, 'Rikers Island' = 144, 'Rosedale' = 145, 'Rossville-Woodrow' = 146, 'Rugby-Remsen Village' = 147, 'Schuylerville-Throgs Neck-Edgewater Park' = 148, 'Seagate-Coney Island' = 149, 'Sheepshead Bay-Gerritsen Beach-Manhattan Beach' = 150, 'SoHo-TriBeCa-Civic Center-Little Italy' = 151, 'Soundview-Bruckner' = 152, 'Soundview-Castle Hill-Clason Point-Harding Park' = 153, 'South Jamaica' = 154, 'South Ozone Park' = 155, 'Springfield Gardens North' = 156, 'Springfield Gardens South-Brookville' = 157, 'Spuyten Duyvil-Kingsbridge' = 158, 'St. Albans' = 159, 'Stapleton-Rosebank' = 160, 'Starrett City' = 161, 'Steinway' = 162, 'Stuyvesant Heights' = 163, 'Stuyvesant Town-Cooper Village' = 164, 'Sunset Park East' = 165, 'Sunset Park West' = 166, 'Todt Hill-Emerson Hill-Heartland Village-Lighthouse Hill' = 167, 'Turtle Bay-East Midtown' = 168, 'University Heights-Morris Heights' = 169, 'Upper East Side-Carnegie Hill' = 170, 'Upper West Side' = 171, 'Van Cortlandt Village' = 172, 'Van Nest-Morris Park-Westchester Square' = 173, 'Washington Heights North' = 174, 'Washington Heights South' = 175, 'West Brighton' = 176, 'West Concourse' = 177, 'West Farms-Bronx River' = 178, 'West New Brighton-New Brighton-St. George' = 179, 'West Village' = 180, 'Westchester-Unionport' = 181, 'Westerleigh' = 182, 'Whitestone' = 183, 'Williamsbridge-Olinville' = 184, 'Williamsburg' = 185, 'Windsor Terrace' = 186, 'Woodhaven' = 187, 'Woodlawn-Wakefield' = 188, 'Woodside' = 189, 'Yorkville' = 190, 'park-cemetery-etc-Bronx' = 191, 'park-cemetery-etc-Brooklyn' = 192, 'park-cemetery-etc-Manhattan' = 193, 'park-cemetery-etc-Queens' = 194, 'park-cemetery-etc-Staten Island' = 195), dropoff_puma UInt16) ENGINE = MergeTree(pickup_date, pickup_datetime, 8192) ``` -
+
بر روی سرور source دستور زیر را وارد کنید: @@ -368,7 +368,7 @@ CREATE TABLE default.trips_mergetree_third ( trip_id UInt32, vendor_id Enum8('1 CREATE TABLE trips_mergetree_x3 AS trips_mergetree_third ENGINE = Distributed(perftest, default, trips_mergetree_third, rand()) ``` -
+
query زیر دادها را توزیع مجدد می کند: @@ -378,7 +378,7 @@ query زیر دادها را توزیع مجدد می کند: INSERT INTO trips_mergetree_x3 SELECT * FROM trips_mergetree ``` -
+
این query 2454 ثانیه زمان میبرد. diff --git a/docs/fa/getting_started/example_datasets/ontime.md b/docs/fa/getting_started/example_datasets/ontime.md index aa60dd57ab0..f5bf5b865af 100644 --- a/docs/fa/getting_started/example_datasets/ontime.md +++ b/docs/fa/getting_started/example_datasets/ontime.md @@ -1,6 +1,6 @@ -
+
# OnTime @@ -18,7 +18,7 @@ done done ``` -
+
(از ) @@ -140,7 +140,7 @@ CREATE TABLE `ontime` ( ) ENGINE = MergeTree(FlightDate, (Year, FlightDate), 8192) ``` -
+
Load داده ها: @@ -150,7 +150,7 @@ Load داده ها: for i in *.zip; do echo $i; unzip -cq $i '*.csv' | sed 's/\.00//g' | clickhouse-client --host=example-perftest01j --query="INSERT INTO ontime FORMAT CSVWithNames"; done ``` -
+
query ها: Q0. @@ -161,7 +161,7 @@ Q0. select avg(c1) from (select Year, Month, count(*) as c1 from ontime group by Year, Month); ``` -
+
Q1. تعداد پروازهای به تفکیک روز از تاریخ 2000 تا 2008 @@ -171,7 +171,7 @@ Q1. تعداد پروازهای به تفکیک روز از تاریخ 2000 تا SELECT DayOfWeek, count(*) AS c FROM ontime WHERE Year >= 2000 AND Year <= 2008 GROUP BY DayOfWeek ORDER BY c DESC; ``` -
+
Q2. تعداد پروازهای بیش از 10 دقیقه تاخیر خورده، گروه بندی براساس روزهای هفته از سال 2000 تا 2008 @@ -181,7 +181,7 @@ Q2. تعداد پروازهای بیش از 10 دقیقه تاخیر خورده SELECT DayOfWeek, count(*) AS c FROM ontime WHERE DepDelay>10 AND Year >= 2000 AND Year <= 2008 GROUP BY DayOfWeek ORDER BY c DESC ``` -
+
Q3. تعداد تاخیرها براساس airport از سال 2000 تا 2008 @@ -191,7 +191,7 @@ Q3. تعداد تاخیرها براساس airport از سال 2000 تا 2008 SELECT Origin, count(*) AS c FROM ontime WHERE DepDelay>10 AND Year >= 2000 AND Year <= 2008 GROUP BY Origin ORDER BY c DESC LIMIT 10 ``` -
+
Q4. تعداد تاخیرها براساس carrier در سال 78 @@ -201,7 +201,7 @@ Q4. تعداد تاخیرها براساس carrier در سال 78 SELECT Carrier, count(*) FROM ontime WHERE DepDelay>10 AND Year = 2007 GROUP BY Carrier ORDER BY count(*) DESC ``` -
+
Q5. درصد تاخیر ها براساس carrier در سال 2007 @@ -231,7 +231,7 @@ ANY INNER JOIN ORDER BY c3 DESC; ``` -
+
نسخه ی بهتر query @@ -241,7 +241,7 @@ ORDER BY c3 DESC; SELECT Carrier, avg(DepDelay > 10) * 1000 AS c3 FROM ontime WHERE Year = 2007 GROUP BY Carrier ORDER BY Carrier ``` -
+
Q6. مانند query قبلی اما برای طیف وسیعی از سال های 2000 تا 2008 @@ -271,7 +271,7 @@ ANY INNER JOIN ORDER BY c3 DESC; ``` -
+
نسخه ی بهتر query @@ -281,7 +281,7 @@ ORDER BY c3 DESC; SELECT Carrier, avg(DepDelay > 10) * 1000 AS c3 FROM ontime WHERE Year >= 2000 AND Year <= 2008 GROUP BY Carrier ORDER BY Carrier ``` -
+
Q7. درصد تاخیر بیش از 10 دقیقه پروازها به تفکیک سال @@ -309,7 +309,7 @@ ANY INNER JOIN ORDER BY Year ``` -
+
نسخه ی بهتر query @@ -319,7 +319,7 @@ ORDER BY Year SELECT Year, avg(DepDelay > 10) FROM ontime GROUP BY Year ORDER BY Year ``` -
+
Q8. مقصدهای پرطرفدار براساس تعداد اتصال های مستقیم شهرها برای سال 2000 تا 2010 @@ -329,7 +329,7 @@ Q8. مقصدهای پرطرفدار براساس تعداد اتصال های م SELECT DestCityName, uniqExact(OriginCityName) AS u FROM ontime WHERE Year >= 2000 and Year <= 2010 GROUP BY DestCityName ORDER BY u DESC LIMIT 10; ``` -
+
Q9. @@ -339,7 +339,7 @@ Q9. select Year, count(*) as c1 from ontime group by Year; ``` -
+
Q10. @@ -361,7 +361,7 @@ ORDER by rate DESC LIMIT 1000; ``` -
+
query های بیشتر: @@ -379,7 +379,7 @@ SELECT OriginCityName, DestCityName, count() AS c FROM ontime GROUP BY OriginCit SELECT OriginCityName, count() AS c FROM ontime GROUP BY OriginCityName ORDER BY c DESC LIMIT 10; ``` -
+
این تست های performance توسط Vadim Tkachenko انجام شده است. برای اطلاعات بیشتر به لینک های زیر مراجعه کنید: diff --git a/docs/fa/getting_started/example_datasets/star_schema.md b/docs/fa/getting_started/example_datasets/star_schema.md index 9a0e55b7655..8d09090f35e 100644 --- a/docs/fa/getting_started/example_datasets/star_schema.md +++ b/docs/fa/getting_started/example_datasets/star_schema.md @@ -1,4 +1,4 @@ -
+
# بنچمارک Star Schema @@ -12,7 +12,7 @@ cd ssb-dbgen make ``` -
+
در هنگام پردازش چند warnings نمایش داده می شود که مشکلی نیست و طبیعی است. @@ -27,7 +27,7 @@ make ./dbgen -s 1000 -T l ``` -
+
ساخت جداول در ClickHouse @@ -84,7 +84,7 @@ CREATE TABLE customerd AS customer ENGINE = Distributed(perftest_3shards_1replic CREATE TABLE partd AS part ENGINE = Distributed(perftest_3shards_1replicas, default, part, rand()); ``` -
+
برای تست بر روی یک سرور، فقط از جداول MergeTree استفاده کنید. برای تست توزیع شده، شما نیاز به کانفیگ `perftest_3shards_1replicas` در فایل کانفیگ را دارید. در ادامه جداول MergeTree را در هر سرور ایجاد کنید و موارد بالا را توزیع کنید. diff --git a/docs/fa/getting_started/example_datasets/wikistat.md b/docs/fa/getting_started/example_datasets/wikistat.md index 59ec74bb836..543a41901ab 100644 --- a/docs/fa/getting_started/example_datasets/wikistat.md +++ b/docs/fa/getting_started/example_datasets/wikistat.md @@ -1,4 +1,4 @@ -
+
# WikiStat @@ -22,7 +22,7 @@ CREATE TABLE wikistat ``` -
+
load دیتا diff --git a/docs/fa/getting_started/index.md b/docs/fa/getting_started/index.md index b10fc0170b5..8d5a0bcfa11 100644 --- a/docs/fa/getting_started/index.md +++ b/docs/fa/getting_started/index.md @@ -1,4 +1,4 @@ -
+
# شروع به کار @@ -12,7 +12,7 @@ grep -q sse4_2 /proc/cpuinfo && echo "SSE 4.2 supported" || echo "SSE 4.2 not supported" ``` -
+
پیشنهاد می کنیم از Ubuntu TrustyT، Ubuntu Xenial یا Ubuntu Precise استفاده کنید. ترمینال باید از UTF-8 پشتیبانی کند. (به صورت پیش فرض در Ubuntu پشتیبانی می شود). @@ -30,7 +30,7 @@ grep -q sse4_2 /proc/cpuinfo && echo "SSE 4.2 supported" || echo "SSE 4.2 not su deb http://repo.yandex.ru/clickhouse/deb/stable/ main/ ``` -
+
اگر شما میخوایید جدیدترین نسخه ی تست را استفاده کنید، 'stable' رو به 'testing' تغییر بدید. @@ -44,7 +44,7 @@ sudo apt-get update sudo apt-get install clickhouse-client clickhouse-server ``` -
+
شما همچنین می توانید از طریق لینک زیر پکیج ClickHouse را به صورت دستی دانلود و نصب کنید: . @@ -63,7 +63,7 @@ Client: dbms/programs/clickhouse-client Server: dbms/programs/clickhouse-server ``` -
+
برای سرور، یک کاتالوگ با دیتا بسازید، مانند @@ -74,7 +74,7 @@ Server: dbms/programs/clickhouse-server /opt/clickhouse/metadata/default/ ``` -
+
(قابل تنظیم در تنظیمات سرور). 'chown' را برای کاربر دلخواه اجرا کنید. @@ -98,7 +98,7 @@ Gentoo: `emerge clickhouse` sudo service clickhouse-server start ``` -
+
لاگ های دایرکتوری `/var/log/clickhouse-server/ directory.` را مشاهده کنید. @@ -112,7 +112,7 @@ sudo service clickhouse-server start clickhouse-server --config-file=/etc/clickhouse-server/config.xml ``` -
+
در این مورد که مناسب زمان توسعه می باشد، لاگ ها در کنسول پرینت می شوند. اگر فایل تنظیمات در دایرکتوری جاری باشد، نیازی به مشخص کردن '--config-file' نمی باشد. به صورت پیش فرض از './config.xml' استفاده می شود. @@ -124,7 +124,7 @@ clickhouse-server --config-file=/etc/clickhouse-server/config.xml clickhouse-client ``` -
+
پارامترهای پیش فرض، نشان از اتصال به localhost:9000 از طرف کاربر 'default' بدون پسورد را می دهد. از کلاینت میتوان برای اتصال به یک سرور remote استفاده کرد. مثال: @@ -134,7 +134,7 @@ clickhouse-client clickhouse-client --host=example.com ``` -
+
برای اطلاعات بیشتر، بخش "کلاینت Command-line" را مشاهده کنید. @@ -161,7 +161,7 @@ SELECT 1 :) ``` -
+
**تبریک میگم، سیستم کار می کنه!** diff --git a/docs/fa/images/column_oriented.gif b/docs/fa/images/column_oriented.gif new file mode 100644 index 0000000000000000000000000000000000000000..15f4b12e697ac40c60bf77f964645316410da946 GIT binary patch literal 45485 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%0Db)&ir^}exmATU_U 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/fa/images/logo.svg b/docs/fa/images/logo.svg new file mode 100644 index 00000000000..70662da887e --- /dev/null +++ b/docs/fa/images/logo.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/docs/fa/images/row_oriented.gif b/docs/fa/images/row_oriented.gif new file mode 100644 index 0000000000000000000000000000000000000000..53daa20f322d37f67652e5f53c26a433ea8cb5c9 GIT binary patch literal 41571 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 QL3NUUYU=-w$V>$P2TZ7nQUCw| literal 0 HcmV?d00001 diff --git a/docs/fa/index.md b/docs/fa/index.md index 65fe55e3025..aa93081b948 100644 --- a/docs/fa/index.md +++ b/docs/fa/index.md @@ -1,4 +1,4 @@ -
+
# ClickHouse چیست؟ diff --git a/docs/fa/interfaces/cli.md b/docs/fa/interfaces/cli.md index f8546c5534a..6c7372c8363 100644 --- a/docs/fa/interfaces/cli.md +++ b/docs/fa/interfaces/cli.md @@ -1,4 +1,4 @@ -
+
# کلاینت Command-line @@ -15,7 +15,7 @@ Connected to ClickHouse server version 0.0.26176. :) ``` -
+
کلاینت از آپشن های command-line و فایل های کانفیگ پشتیبانی می کند. برای اطلاعات بیشتر بخش "[پیکربندی](#interfaces_cli_configuration)" را مشاهده کنید. @@ -37,7 +37,7 @@ _EOF cat file.csv | clickhouse-client --database=test --query="INSERT INTO test FORMAT CSV"; ``` -
+
در حالت Batch، فرمت داده ها به صورت پیش فرض به صورت TabSeparated می باشد. شما میتوانید فرمت داده ها رو در هنگام اجرای query و با استفاده از شرط FORMAT مشخص کنید. diff --git a/docs/fa/interfaces/formats.md b/docs/fa/interfaces/formats.md index 9e8988abdff..5e7182152d9 100644 --- a/docs/fa/interfaces/formats.md +++ b/docs/fa/interfaces/formats.md @@ -1,6 +1,6 @@ -
+
# فرمت های Input و Output @@ -49,7 +49,7 @@ SELECT SearchPhrase, count() AS c FROM test.hits GROUP BY SearchPhrase FORMAT CapnProto SETTINGS schema = 'schema:Message' ``` -
+
جایی که `schema.capnp` شبیه این است: @@ -62,7 +62,7 @@ struct Message { } ``` -
+
فایل های Schema در فایلی قرار دارند که این فایل در دایرکتوری مشخص شده کانفیگ [ format_schema_path](../operations/server_settings/settings.md#server_settings-format_schema_path) قرار گرفته اند. @@ -80,7 +80,7 @@ Comma Separated Values format ([RFC](https://tools.ietf.org/html/rfc4180)). clickhouse-client --format_csv_delimiter="|" --query="INSERT INTO test.csv FORMAT CSV" < data.csv ``` -
+
*به صورت پیش فرض — `,`. برای اطلاعات بیشتر [format_csv_delimiter](/operations/settings/settings/#format_csv_delimiter) را ببینید. @@ -166,7 +166,7 @@ SELECT SearchPhrase, count() AS c FROM test.hits GROUP BY SearchPhrase WITH TOTA } ``` -
+
JSON با جاوااسکریپت سازگار است. برای اطمینان از این، بعضی از کاراکتر ها ecape های اضافه دارند: اسلش `/` به صورت `\/` escape می شود؛ line break جایگزین یعنی `U+2028` و `U+2029` که باعث break در بعضی از مروگرها می شود، به شکل `\uXXXX` escape می شوند. کاراکتر های کنترلی ASCII هم escape می شوند: backspace، form feed، line feed، carriage return، و horizontal tab به ترتیب با `\b`، `\f`، `\n`، `\r`، `\t` جایگزین می شوند. همچنین بایت های باقی مانده در محدوده 00 تا 1F با استفاده از `\uXXXX` جایگزین می شوند. کاراکتر های بی اعتبار UTF-8 با � جایگزین می شوند، پس خروجی JSON شامل موارد معتبر UTF-8 می باشد. برای سازگاری با جاوااسکریپت، اعداد Int64 و Uint64 به صورت پیش فرض، با استفاده از دابل کوتیشن enclose می شوند. برای حذف کوتیشن، شما باید پارامتر output_format_json_quote_64bit_integers v رو برابر با 0 قرار دهید. @@ -225,7 +225,7 @@ JSON با جاوااسکریپت سازگار است. برای اطمینان ا } ``` -
+
این فرمت فقط مناسب خروجی query های می باشد، به این معنی که برای عملیات پارس کردن (دریافت داده برای insert در جدول) نیست. همچنین فرمت JSONEachRow را ببینید. @@ -248,7 +248,7 @@ JSON با جاوااسکریپت سازگار است. برای اطمینان ا {"SearchPhrase":"baku","count()":"1000"} ``` -
+
بر خلاف فرمت JSON، هیچ جایگزینی برای کاراکتر های بی اعتبار UTF-8 وجود ندارد. هر مجموعه ای از بایت های می تواند داخل سطر در خروجی باشند. پس داده ها بدون از دست دادن هیچ اطلاعاتی فرمت می شوند. مقادیر شبیه به JSON، escape می شوند. @@ -299,7 +299,7 @@ Extremes: └────────────┴─────────┘ ``` -
+
## PrettyCompact @@ -321,7 +321,7 @@ Extremes: watch -n1 "clickhouse-client --query='SELECT * FROM system.events FORMAT PrettyCompactNoEscapes'" ``` -
+
شما می توانید برای نمایش در مرورگر از interface HTTP استفاده کنید. @@ -370,7 +370,7 @@ Hello\ world ``` -
+
نوع دوم به دلیل پشتیبانی MySQL در هنگام نوشتن دامپ به صورت tab-separate، پشتیبانی می شود. @@ -405,7 +405,7 @@ SELECT EventDate, count() AS c FROM test.hits GROUP BY EventDate WITH TOTALS ORD 2014-03-23 1406958 ``` -
+
این فرمت نیز تحت نام `TSV` موجود است. @@ -448,7 +448,7 @@ SearchPhrase=curtain design count()=1064 SearchPhrase=baku count()=1000 ``` -
+
وقتی تعداد زیادی از ستون ها وجود دارد، این فرمت بی فایده است، و در حالت کلی دلیلی بر استفاده از این فرمت در این مواقع وجود ندارد. این فرمت در بعضی از دپارتمان های Yandex استفاده می شد. @@ -489,7 +489,7 @@ test: string with 'quotes' and with some special characters ``` -
+
در مقایسه با فرمت Vertical: @@ -503,7 +503,7 @@ test: string with \'quotes\' and \t with some special \n characters ``` ## XML -
+
فرمت XML فقط برای خروجی مناسب است، نه برای پارس کردن. مثال: @@ -571,7 +571,7 @@ test: string with \'quotes\' and \t with some special \n characters ``` -
+
اگر نام فیلد، فرمت قابل قبولی نداشته باشد، اسم 'field' به عنوان نام عنصر استفاده می شود. به طور کلی، ساختار XML مشابه ساختار JSON می باشد. فقط در JSON، موارد بی اعتبار UTF-8 تبدیل به کاراکتر � می شوند که منجر به خروجی معتبر UTF-8 می شود. diff --git a/docs/fa/interfaces/http_interface.md b/docs/fa/interfaces/http_interface.md index caabee64552..3251b2d4ccd 100644 --- a/docs/fa/interfaces/http_interface.md +++ b/docs/fa/interfaces/http_interface.md @@ -1,4 +1,4 @@ -
+
# HTTP interface @@ -13,7 +13,7 @@ $ curl 'http://localhost:8123/' Ok. ``` -
+
درخواست های خود را پارامتر 'query'، یا با متد POST، یا ابتدای query را در پارامتر 'query' ارسال کنید، و بقیه را در POST (بعدا توضیح خواهیم داد که چرا این کار ضروری است). سایت URL محدود به 16 کیلوبایت است، پس هنگام ارسال query های بزرگ، اینو به خاطر داشته باشید @@ -40,7 +40,7 @@ Date: Fri, 16 Nov 2012 19:21:50 GMT 1 ``` -
+
همانطور که می بینید، curl is somewhat inconvenient in that spaces must be URL escaped. هر چند wget همه چیز را خودش escape می کنه، ما توصیه به استفاده از اون رو نمی کنیم، چون wget به خوبی با HTTP 1.1 در هنگام استفاده از هدر های keep-alive و Transfer-Encoding: chunked کار نمی کند. @@ -57,7 +57,7 @@ $ echo '1' | curl 'http://localhost:8123/?query=SELECT' --data-binary @- 1 ``` -
+
اگر بخشی از query در پارامتر ارسال شود، و بخش دیگر در POST، یک line feed بین دو بخش وارد می شود. مثال (این کار نمی کند): @@ -70,7 +70,7 @@ ECT 1 , expected One of: SHOW TABLES, SHOW DATABASES, SELECT, INSERT, CREATE, ATTACH, RENAME, DROP, DETACH, USE, SET, OPTIMIZE., e.what() = DB::Exception ``` -
+
به صورت پیش فرض، داده ها با فرمت TabSeparated بر میگردند. (برای اطلاعات بیشتر بخش "فرمت" را مشاهده کنید). شما میتوانید از دستور FORMAT در query خود برای ست کردن فرمتی دیگر استفاده کنید. @@ -85,7 +85,7 @@ $ echo 'SELECT 1 FORMAT Pretty' | curl 'http://localhost:8123/?' --data-binary @ └───┘ ``` -
+
برای query های INSERT متد POST ضروری است. در این مورد، شما می توانید ابتدای query خود را در URL parameter بنویسید، و از POST برای پاس داده داده ها برای درج استفاده کنید. داده ی برای درج می تواند، برای مثال یک دامپ tab-separated شده از MySQL باشد. به این ترتیب، query INSERT جایگزین LOAD DATA LOCAL INFILE از MySQL می شود. @@ -97,7 +97,7 @@ $ echo 'SELECT 1 FORMAT Pretty' | curl 'http://localhost:8123/?' --data-binary @ echo 'CREATE TABLE t (a UInt8) ENGINE = Memory' | curl 'http://localhost:8123/' --data-binary @- ``` -
+
استفاده از query INSERT برای درج داده: @@ -107,7 +107,7 @@ echo 'CREATE TABLE t (a UInt8) ENGINE = Memory' | curl 'http://localhost:8123/' echo 'INSERT INTO t VALUES (1),(2),(3)' | curl 'http://localhost:8123/' --data-binary @- ``` -
+
داده ها میتوانند جدا از پارامتر query ارسال شوند: @@ -117,7 +117,7 @@ echo 'INSERT INTO t VALUES (1),(2),(3)' | curl 'http://localhost:8123/' --data-b echo '(4),(5),(6)' | curl 'http://localhost:8123/?query=INSERT%20INTO%20t%20VALUES' --data-binary @- ``` -
+
شما می توانید هر نوع فرمت دیتایی مشخص کنید. فرمت 'Values' دقیقا مشابه زمانی است که شما INSERT INTO t VALUES را می نویسید: @@ -127,7 +127,7 @@ echo '(4),(5),(6)' | curl 'http://localhost:8123/?query=INSERT%20INTO%20t%20VALU echo '(7),(8),(9)' | curl 'http://localhost:8123/?query=INSERT%20INTO%20t%20FORMAT%20Values' --data-binary @- ``` -
+
برای درج داده ها از یک دامپ tab-separate، فرمت مشخص زیر را وارد کنید: @@ -137,7 +137,7 @@ echo '(7),(8),(9)' | curl 'http://localhost:8123/?query=INSERT%20INTO%20t%20FORM echo -ne '10\n11\n12\n' | curl 'http://localhost:8123/?query=INSERT%20INTO%20t%20FORMAT%20TabSeparated' --data-binary @- ``` -
+
به دلیل پردازش موازی، نتایج query با ترتیب رندوم چاپ می شود: @@ -159,7 +159,7 @@ $ curl 'http://localhost:8123/?query=SELECT%20a%20FROM%20t' 6 ``` -
+
حدف جدول: @@ -169,7 +169,7 @@ $ curl 'http://localhost:8123/?query=SELECT%20a%20FROM%20t' echo 'DROP TABLE t' | curl 'http://localhost:8123/' --data-binary @- ``` -
+
برای درخواست هایی موفقی که داده ای از جدول بر نمیگردد، بدنه response خالی است. @@ -199,7 +199,7 @@ $ echo 'SELECT number FROM numbers LIMIT 10' | curl 'http://localhost:8123/?data 9 ``` -
+
به صورت پیش فرض، دیتابیس ثبت شده در تنظیمات سرور به عنوان دیتابیس پیش فرض مورد استفاده قرار می گیرد، این دیتابیس 'default' نامیده می شود. از سوی دیگر، شما می توانید همیشه نام دیتابیس را با دات و قبل از اسم جدول مشخص کنید. @@ -213,7 +213,7 @@ $ echo 'SELECT number FROM numbers LIMIT 10' | curl 'http://localhost:8123/?data echo 'SELECT 1' | curl 'http://user:password@localhost:8123/' -d @- ``` -
+
2. با دو پارامتر 'user' و 'password' در URL. مثال: @@ -223,7 +223,7 @@ echo 'SELECT 1' | curl 'http://user:password@localhost:8123/' -d @- echo 'SELECT 1' | curl 'http://localhost:8123/?user=user&password=password' -d @- ``` -
+
اگر نام کاربری مشخص نشود، نام کاربری 'default' استفاده می شود. اگر پسورد مشخص نشود، پسورد خالی استفاده می شود. شما همچنین می توانید از پارامتر های URL برای مشخص کردن هر تنظیمی برای اجرای یک query استفاده کنید. مثال: http://localhost:8123/?profile=web&max_rows_to_read=1000000000&query=SELECT+1 @@ -245,7 +245,7 @@ $ echo 'SELECT number FROM system.numbers LIMIT 10' | curl 'http://localhost:812 9 ``` -
+
برای اطلاعات بیشتر در مورد دیگر پارامترها، بخش "SET" را ببینید. @@ -275,8 +275,8 @@ HTTP interface اجازه ی پاس دادن داده های external (جداو curl -sS 'http://localhost:8123/?max_result_bytes=4000000&buffer_size=3000000&wait_end_of_query=1' -d 'SELECT toUInt8(number) FROM system.numbers LIMIT 9000000 FORMAT RowBinary' ``` -
+
از بافرینگ به منظور اجتناب از شرایطی که یک خطای پردازش query رخ داده بعد از response کد و هدر های ارسال شده به کلاینت استفاده کنید. در این شرایط، پیغام خطا در انتهای بنده response نوشته می شود، و در سمت کلاینت، پیغام خطا فقط از طریق مرحله پارس کردن قابل شناسایی است. -
\ No newline at end of file +
diff --git a/docs/fa/interfaces/index.md b/docs/fa/interfaces/index.md index 73cc3a71111..4d4600bd26f 100644 --- a/docs/fa/interfaces/index.md +++ b/docs/fa/interfaces/index.md @@ -1,6 +1,6 @@ -
+
# Interface ها diff --git a/docs/fa/interfaces/jdbc.md b/docs/fa/interfaces/jdbc.md index c8a2f264ca7..dc0acb15994 100644 --- a/docs/fa/interfaces/jdbc.md +++ b/docs/fa/interfaces/jdbc.md @@ -1,4 +1,4 @@ -
+
# درایور JDBC diff --git a/docs/fa/interfaces/tcp.md b/docs/fa/interfaces/tcp.md index 2d1802cf3e5..963555b5400 100644 --- a/docs/fa/interfaces/tcp.md +++ b/docs/fa/interfaces/tcp.md @@ -1,4 +1,4 @@ -
+
# Native interface (TCP) diff --git a/docs/fa/interfaces/third-party_client_libraries.md b/docs/fa/interfaces/third-party_client_libraries.md index fd03b5cc98a..2fb5de3cc9b 100644 --- a/docs/fa/interfaces/third-party_client_libraries.md +++ b/docs/fa/interfaces/third-party_client_libraries.md @@ -1,4 +1,4 @@ -
+
# کتابخانه های توسعه دهندگان third-party diff --git a/docs/fa/interfaces/third-party_gui.md b/docs/fa/interfaces/third-party_gui.md index 016c05d7f70..8972edd880a 100644 --- a/docs/fa/interfaces/third-party_gui.md +++ b/docs/fa/interfaces/third-party_gui.md @@ -1,4 +1,4 @@ -
+
# interface های visual توسعه دهندگان third-party diff --git a/docs/fa/introduction/distinctive_features.md b/docs/fa/introduction/distinctive_features.md index c74d44fd844..61b2115a3b5 100644 --- a/docs/fa/introduction/distinctive_features.md +++ b/docs/fa/introduction/distinctive_features.md @@ -1,4 +1,4 @@ -
+
# ویژگی های برجسته ClickHouse diff --git a/docs/fa/introduction/features_considered_disadvantages.md b/docs/fa/introduction/features_considered_disadvantages.md index ab28a7a331a..f3984cf1883 100644 --- a/docs/fa/introduction/features_considered_disadvantages.md +++ b/docs/fa/introduction/features_considered_disadvantages.md @@ -1,4 +1,4 @@ -
+
# ویژگی های از ClickHouse که می تواند معایبی باشد. diff --git a/docs/fa/introduction/performance.md b/docs/fa/introduction/performance.md index 4e8b4f064a8..8d738cdbf9f 100644 --- a/docs/fa/introduction/performance.md +++ b/docs/fa/introduction/performance.md @@ -1,4 +1,4 @@ -
+
# Performance diff --git a/docs/fa/introduction/ya_metrika_task.md b/docs/fa/introduction/ya_metrika_task.md index c7c8c8f5286..d1f4aa1d1fd 100644 --- a/docs/fa/introduction/ya_metrika_task.md +++ b/docs/fa/introduction/ya_metrika_task.md @@ -1,4 +1,4 @@ -
+
# Yandex.Metrica use case diff --git a/docs/fa/operations/access_rights.md b/docs/fa/operations/access_rights.md new file mode 120000 index 00000000000..73463029569 --- /dev/null +++ b/docs/fa/operations/access_rights.md @@ -0,0 +1 @@ +../../en/operations/access_rights.md \ No newline at end of file diff --git a/docs/fa/operations/configuration_files.md b/docs/fa/operations/configuration_files.md new file mode 120000 index 00000000000..a2d73dbaa25 --- /dev/null +++ b/docs/fa/operations/configuration_files.md @@ -0,0 +1 @@ +../../en/operations/configuration_files.md \ No newline at end of file diff --git a/docs/fa/operations/index.md b/docs/fa/operations/index.md new file mode 120000 index 00000000000..f7012cd713b --- /dev/null +++ b/docs/fa/operations/index.md @@ -0,0 +1 @@ +../../en/operations/settings/index.md \ No newline at end of file diff --git a/docs/fa/operations/quotas.md b/docs/fa/operations/quotas.md new file mode 120000 index 00000000000..1c52cdf1e91 --- /dev/null +++ b/docs/fa/operations/quotas.md @@ -0,0 +1 @@ +../../en/operations/quotas.md \ No newline at end of file diff --git a/docs/fa/operations/server_settings/index.md b/docs/fa/operations/server_settings/index.md new file mode 120000 index 00000000000..1d1a0585a42 --- /dev/null +++ b/docs/fa/operations/server_settings/index.md @@ -0,0 +1 @@ +../../../en/operations/server_settings/index.md \ No newline at end of file diff --git a/docs/fa/operations/server_settings/settings.md b/docs/fa/operations/server_settings/settings.md new file mode 120000 index 00000000000..19cd2e82ce7 --- /dev/null +++ b/docs/fa/operations/server_settings/settings.md @@ -0,0 +1 @@ +../../../en/operations/server_settings/settings.md \ No newline at end of file diff --git a/docs/fa/operations/settings/index.md b/docs/fa/operations/settings/index.md new file mode 120000 index 00000000000..fc3968d1f1e --- /dev/null +++ b/docs/fa/operations/settings/index.md @@ -0,0 +1 @@ +../../../en/operations/settings/index.md \ No newline at end of file diff --git a/docs/fa/operations/settings/query_complexity.md b/docs/fa/operations/settings/query_complexity.md new file mode 120000 index 00000000000..9a9c6d975a9 --- /dev/null +++ b/docs/fa/operations/settings/query_complexity.md @@ -0,0 +1 @@ +../../../en/operations/settings/query_complexity.md \ No newline at end of file diff --git a/docs/fa/operations/settings/settings.md b/docs/fa/operations/settings/settings.md new file mode 120000 index 00000000000..0c8df3cfc90 --- /dev/null +++ b/docs/fa/operations/settings/settings.md @@ -0,0 +1 @@ +../../../en/operations/settings/settings.md \ No newline at end of file diff --git a/docs/fa/operations/settings/settings_profiles.md b/docs/fa/operations/settings/settings_profiles.md new file mode 120000 index 00000000000..35d9747ad56 --- /dev/null +++ b/docs/fa/operations/settings/settings_profiles.md @@ -0,0 +1 @@ +../../../en/operations/settings/settings_profiles.md \ No newline at end of file diff --git a/docs/fa/operations/system_tables.md b/docs/fa/operations/system_tables.md new file mode 120000 index 00000000000..c5701190dca --- /dev/null +++ b/docs/fa/operations/system_tables.md @@ -0,0 +1 @@ +../../en/operations/system_tables.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/aggregatingmergetree.md b/docs/fa/operations/table_engines/aggregatingmergetree.md new file mode 120000 index 00000000000..907a073e0c8 --- /dev/null +++ b/docs/fa/operations/table_engines/aggregatingmergetree.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/aggregatingmergetree.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/buffer.md b/docs/fa/operations/table_engines/buffer.md new file mode 120000 index 00000000000..0a3c372fa67 --- /dev/null +++ b/docs/fa/operations/table_engines/buffer.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/buffer.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/collapsingmergetree.md b/docs/fa/operations/table_engines/collapsingmergetree.md new file mode 120000 index 00000000000..ef5cebb48d8 --- /dev/null +++ b/docs/fa/operations/table_engines/collapsingmergetree.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/collapsingmergetree.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/custom_partitioning_key.md b/docs/fa/operations/table_engines/custom_partitioning_key.md new file mode 120000 index 00000000000..a9d18cacb25 --- /dev/null +++ b/docs/fa/operations/table_engines/custom_partitioning_key.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/custom_partitioning_key.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/dictionary.md b/docs/fa/operations/table_engines/dictionary.md new file mode 120000 index 00000000000..2a95f4a669b --- /dev/null +++ b/docs/fa/operations/table_engines/dictionary.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/dictionary.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/distributed.md b/docs/fa/operations/table_engines/distributed.md new file mode 120000 index 00000000000..46994303c35 --- /dev/null +++ b/docs/fa/operations/table_engines/distributed.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/distributed.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/external_data.md b/docs/fa/operations/table_engines/external_data.md new file mode 120000 index 00000000000..27a7b6acec2 --- /dev/null +++ b/docs/fa/operations/table_engines/external_data.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/external_data.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/file.md b/docs/fa/operations/table_engines/file.md new file mode 120000 index 00000000000..27dffc8d78f --- /dev/null +++ b/docs/fa/operations/table_engines/file.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/file.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/graphitemergetree.md b/docs/fa/operations/table_engines/graphitemergetree.md new file mode 120000 index 00000000000..654425d050a --- /dev/null +++ b/docs/fa/operations/table_engines/graphitemergetree.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/graphitemergetree.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/index.md b/docs/fa/operations/table_engines/index.md new file mode 120000 index 00000000000..994dff9b516 --- /dev/null +++ b/docs/fa/operations/table_engines/index.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/index.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/join.md b/docs/fa/operations/table_engines/join.md new file mode 120000 index 00000000000..0914ab950ed --- /dev/null +++ b/docs/fa/operations/table_engines/join.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/join.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/kafka.md b/docs/fa/operations/table_engines/kafka.md new file mode 120000 index 00000000000..cb7bd5dd0f8 --- /dev/null +++ b/docs/fa/operations/table_engines/kafka.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/kafka.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/log.md b/docs/fa/operations/table_engines/log.md new file mode 120000 index 00000000000..2c39ba68522 --- /dev/null +++ b/docs/fa/operations/table_engines/log.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/log.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/materializedview.md b/docs/fa/operations/table_engines/materializedview.md new file mode 120000 index 00000000000..e3b5deb73dc --- /dev/null +++ b/docs/fa/operations/table_engines/materializedview.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/materializedview.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/memory.md b/docs/fa/operations/table_engines/memory.md new file mode 120000 index 00000000000..eee940c7bd3 --- /dev/null +++ b/docs/fa/operations/table_engines/memory.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/memory.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/merge.md b/docs/fa/operations/table_engines/merge.md new file mode 120000 index 00000000000..9e17d9bb939 --- /dev/null +++ b/docs/fa/operations/table_engines/merge.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/merge.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/mergetree.md b/docs/fa/operations/table_engines/mergetree.md new file mode 120000 index 00000000000..cc6ac1e5297 --- /dev/null +++ b/docs/fa/operations/table_engines/mergetree.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/mergetree.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/mysql.md b/docs/fa/operations/table_engines/mysql.md new file mode 120000 index 00000000000..e4c268658cf --- /dev/null +++ b/docs/fa/operations/table_engines/mysql.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/mysql.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/null.md b/docs/fa/operations/table_engines/null.md new file mode 120000 index 00000000000..c7d9264571e --- /dev/null +++ b/docs/fa/operations/table_engines/null.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/null.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/replacingmergetree.md b/docs/fa/operations/table_engines/replacingmergetree.md new file mode 120000 index 00000000000..63ff25a4dd6 --- /dev/null +++ b/docs/fa/operations/table_engines/replacingmergetree.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/replacingmergetree.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/replication.md b/docs/fa/operations/table_engines/replication.md new file mode 120000 index 00000000000..b4b22ac708b --- /dev/null +++ b/docs/fa/operations/table_engines/replication.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/replication.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/set.md b/docs/fa/operations/table_engines/set.md new file mode 120000 index 00000000000..d37e659badd --- /dev/null +++ b/docs/fa/operations/table_engines/set.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/set.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/summingmergetree.md b/docs/fa/operations/table_engines/summingmergetree.md new file mode 120000 index 00000000000..2b67e953d8a --- /dev/null +++ b/docs/fa/operations/table_engines/summingmergetree.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/summingmergetree.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/tinylog.md b/docs/fa/operations/table_engines/tinylog.md new file mode 120000 index 00000000000..bda90c7d5ce --- /dev/null +++ b/docs/fa/operations/table_engines/tinylog.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/tinylog.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/url.md b/docs/fa/operations/table_engines/url.md new file mode 120000 index 00000000000..d0de71dcf40 --- /dev/null +++ b/docs/fa/operations/table_engines/url.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/url.md \ No newline at end of file diff --git a/docs/fa/operations/table_engines/view.md b/docs/fa/operations/table_engines/view.md new file mode 120000 index 00000000000..3f2164181a7 --- /dev/null +++ b/docs/fa/operations/table_engines/view.md @@ -0,0 +1 @@ +../../../en/operations/table_engines/view.md \ No newline at end of file diff --git a/docs/fa/operations/tips.md b/docs/fa/operations/tips.md new file mode 120000 index 00000000000..9b3413bdbc3 --- /dev/null +++ b/docs/fa/operations/tips.md @@ -0,0 +1 @@ +../../en/operations/tips.md \ No newline at end of file diff --git a/docs/fa/operations/utils/clickhouse-copier.md b/docs/fa/operations/utils/clickhouse-copier.md new file mode 120000 index 00000000000..c9e89e33c7b --- /dev/null +++ b/docs/fa/operations/utils/clickhouse-copier.md @@ -0,0 +1 @@ +../../../en/operations/utils/clickhouse-copier.md \ No newline at end of file diff --git a/docs/fa/operations/utils/clickhouse-local.md b/docs/fa/operations/utils/clickhouse-local.md new file mode 120000 index 00000000000..032aaaa2b84 --- /dev/null +++ b/docs/fa/operations/utils/clickhouse-local.md @@ -0,0 +1 @@ +../../../en/operations/utils/clickhouse-local.md \ No newline at end of file diff --git a/docs/fa/operations/utils/index.md b/docs/fa/operations/utils/index.md new file mode 120000 index 00000000000..dd089d1ef4b --- /dev/null +++ b/docs/fa/operations/utils/index.md @@ -0,0 +1 @@ +../../../en/operations/utils/index.md \ No newline at end of file diff --git a/docs/fa/query_language/agg_functions/combinators.md b/docs/fa/query_language/agg_functions/combinators.md new file mode 120000 index 00000000000..2b914cebd15 --- /dev/null +++ b/docs/fa/query_language/agg_functions/combinators.md @@ -0,0 +1 @@ +../../../en/query_language/agg_functions/combinators.md \ No newline at end of file diff --git a/docs/fa/query_language/agg_functions/index.md b/docs/fa/query_language/agg_functions/index.md new file mode 120000 index 00000000000..2fcf67abdeb --- /dev/null +++ b/docs/fa/query_language/agg_functions/index.md @@ -0,0 +1 @@ +../../../en/query_language/agg_functions/index.md \ No newline at end of file diff --git a/docs/fa/query_language/agg_functions/parametric_functions.md b/docs/fa/query_language/agg_functions/parametric_functions.md new file mode 120000 index 00000000000..fd3ffafcc5b --- /dev/null +++ b/docs/fa/query_language/agg_functions/parametric_functions.md @@ -0,0 +1 @@ +../../../en/query_language/agg_functions/parametric_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/agg_functions/reference.md b/docs/fa/query_language/agg_functions/reference.md new file mode 120000 index 00000000000..c5651cb0793 --- /dev/null +++ b/docs/fa/query_language/agg_functions/reference.md @@ -0,0 +1 @@ +../../../en/query_language/agg_functions/reference.md \ No newline at end of file diff --git a/docs/fa/query_language/alter.md b/docs/fa/query_language/alter.md new file mode 120000 index 00000000000..44f4ecf9737 --- /dev/null +++ b/docs/fa/query_language/alter.md @@ -0,0 +1 @@ +../../en/query_language/alter.md \ No newline at end of file diff --git a/docs/fa/query_language/create.md b/docs/fa/query_language/create.md new file mode 120000 index 00000000000..a13304d176e --- /dev/null +++ b/docs/fa/query_language/create.md @@ -0,0 +1 @@ +../../en/query_language/create.md \ No newline at end of file diff --git a/docs/fa/query_language/dicts/external_dicts.md b/docs/fa/query_language/dicts/external_dicts.md new file mode 120000 index 00000000000..491b94bffe6 --- /dev/null +++ b/docs/fa/query_language/dicts/external_dicts.md @@ -0,0 +1 @@ +../../../en/query_language/dicts/external_dicts.md \ No newline at end of file diff --git a/docs/fa/query_language/dicts/external_dicts_dict.md b/docs/fa/query_language/dicts/external_dicts_dict.md new file mode 120000 index 00000000000..e27820fee60 --- /dev/null +++ b/docs/fa/query_language/dicts/external_dicts_dict.md @@ -0,0 +1 @@ +../../../en/query_language/dicts/external_dicts_dict.md \ No newline at end of file diff --git a/docs/fa/query_language/dicts/external_dicts_dict_layout.md b/docs/fa/query_language/dicts/external_dicts_dict_layout.md new file mode 120000 index 00000000000..e391c5be723 --- /dev/null +++ b/docs/fa/query_language/dicts/external_dicts_dict_layout.md @@ -0,0 +1 @@ +../../../en/query_language/dicts/external_dicts_dict_layout.md \ No newline at end of file diff --git a/docs/fa/query_language/dicts/external_dicts_dict_lifetime.md b/docs/fa/query_language/dicts/external_dicts_dict_lifetime.md new file mode 120000 index 00000000000..03b53c09077 --- /dev/null +++ b/docs/fa/query_language/dicts/external_dicts_dict_lifetime.md @@ -0,0 +1 @@ +../../../en/query_language/dicts/external_dicts_dict_lifetime.md \ No newline at end of file diff --git a/docs/fa/query_language/dicts/external_dicts_dict_sources.md b/docs/fa/query_language/dicts/external_dicts_dict_sources.md new file mode 120000 index 00000000000..d4f4bf8ef3e --- /dev/null +++ b/docs/fa/query_language/dicts/external_dicts_dict_sources.md @@ -0,0 +1 @@ +../../../en/query_language/dicts/external_dicts_dict_sources.md \ No newline at end of file diff --git a/docs/fa/query_language/dicts/external_dicts_dict_structure.md b/docs/fa/query_language/dicts/external_dicts_dict_structure.md new file mode 120000 index 00000000000..69ff759caea --- /dev/null +++ b/docs/fa/query_language/dicts/external_dicts_dict_structure.md @@ -0,0 +1 @@ +../../../en/query_language/dicts/external_dicts_dict_structure.md \ No newline at end of file diff --git a/docs/fa/query_language/dicts/index.md b/docs/fa/query_language/dicts/index.md new file mode 120000 index 00000000000..fdc188ca2a2 --- /dev/null +++ b/docs/fa/query_language/dicts/index.md @@ -0,0 +1 @@ +../../../en/query_language/dicts/index.md \ No newline at end of file diff --git a/docs/fa/query_language/dicts/internal_dicts.md b/docs/fa/query_language/dicts/internal_dicts.md new file mode 120000 index 00000000000..3f9408dcd45 --- /dev/null +++ b/docs/fa/query_language/dicts/internal_dicts.md @@ -0,0 +1 @@ +../../../en/query_language/dicts/internal_dicts.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/arithmetic_functions.md b/docs/fa/query_language/functions/arithmetic_functions.md new file mode 120000 index 00000000000..c22acb8c7f5 --- /dev/null +++ b/docs/fa/query_language/functions/arithmetic_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/arithmetic_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/array_functions.md b/docs/fa/query_language/functions/array_functions.md new file mode 120000 index 00000000000..268b2295a97 --- /dev/null +++ b/docs/fa/query_language/functions/array_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/array_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/array_join.md b/docs/fa/query_language/functions/array_join.md new file mode 120000 index 00000000000..b100dac784d --- /dev/null +++ b/docs/fa/query_language/functions/array_join.md @@ -0,0 +1 @@ +../../../en/query_language/functions/array_join.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/bit_functions.md b/docs/fa/query_language/functions/bit_functions.md new file mode 120000 index 00000000000..b5cccd0c56c --- /dev/null +++ b/docs/fa/query_language/functions/bit_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/bit_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/comparison_functions.md b/docs/fa/query_language/functions/comparison_functions.md new file mode 120000 index 00000000000..417c589867c --- /dev/null +++ b/docs/fa/query_language/functions/comparison_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/comparison_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/conditional_functions.md b/docs/fa/query_language/functions/conditional_functions.md new file mode 120000 index 00000000000..ad0d775dbb5 --- /dev/null +++ b/docs/fa/query_language/functions/conditional_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/conditional_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/date_time_functions.md b/docs/fa/query_language/functions/date_time_functions.md new file mode 120000 index 00000000000..d11b9b8bb6b --- /dev/null +++ b/docs/fa/query_language/functions/date_time_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/date_time_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/encoding_functions.md b/docs/fa/query_language/functions/encoding_functions.md new file mode 120000 index 00000000000..b2e6be1405b --- /dev/null +++ b/docs/fa/query_language/functions/encoding_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/encoding_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/ext_dict_functions.md b/docs/fa/query_language/functions/ext_dict_functions.md new file mode 120000 index 00000000000..6318f900e4b --- /dev/null +++ b/docs/fa/query_language/functions/ext_dict_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/ext_dict_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/hash_functions.md b/docs/fa/query_language/functions/hash_functions.md new file mode 120000 index 00000000000..90de8ba97e7 --- /dev/null +++ b/docs/fa/query_language/functions/hash_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/hash_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/higher_order_functions.md b/docs/fa/query_language/functions/higher_order_functions.md new file mode 120000 index 00000000000..077feba2a3e --- /dev/null +++ b/docs/fa/query_language/functions/higher_order_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/higher_order_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/in_functions.md b/docs/fa/query_language/functions/in_functions.md new file mode 120000 index 00000000000..3ae5f24dbca --- /dev/null +++ b/docs/fa/query_language/functions/in_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/in_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/index.md b/docs/fa/query_language/functions/index.md new file mode 120000 index 00000000000..a4e9d619cc0 --- /dev/null +++ b/docs/fa/query_language/functions/index.md @@ -0,0 +1 @@ +../../../en/query_language/functions/index.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/ip_address_functions.md b/docs/fa/query_language/functions/ip_address_functions.md new file mode 120000 index 00000000000..b58175a7cdf --- /dev/null +++ b/docs/fa/query_language/functions/ip_address_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/ip_address_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/json_functions.md b/docs/fa/query_language/functions/json_functions.md new file mode 120000 index 00000000000..1b37184e006 --- /dev/null +++ b/docs/fa/query_language/functions/json_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/json_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/logical_functions.md b/docs/fa/query_language/functions/logical_functions.md new file mode 120000 index 00000000000..32015440e09 --- /dev/null +++ b/docs/fa/query_language/functions/logical_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/logical_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/math_functions.md b/docs/fa/query_language/functions/math_functions.md new file mode 120000 index 00000000000..e01674eca4d --- /dev/null +++ b/docs/fa/query_language/functions/math_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/math_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/other_functions.md b/docs/fa/query_language/functions/other_functions.md new file mode 120000 index 00000000000..65164784ced --- /dev/null +++ b/docs/fa/query_language/functions/other_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/other_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/random_functions.md b/docs/fa/query_language/functions/random_functions.md new file mode 120000 index 00000000000..b873e0c86ac --- /dev/null +++ b/docs/fa/query_language/functions/random_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/random_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/rounding_functions.md b/docs/fa/query_language/functions/rounding_functions.md new file mode 120000 index 00000000000..e1217e3b25a --- /dev/null +++ b/docs/fa/query_language/functions/rounding_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/rounding_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/splitting_merging_functions.md b/docs/fa/query_language/functions/splitting_merging_functions.md new file mode 120000 index 00000000000..5f8771abdec --- /dev/null +++ b/docs/fa/query_language/functions/splitting_merging_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/splitting_merging_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/string_functions.md b/docs/fa/query_language/functions/string_functions.md new file mode 120000 index 00000000000..cc4104aaf53 --- /dev/null +++ b/docs/fa/query_language/functions/string_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/string_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/string_replace_functions.md b/docs/fa/query_language/functions/string_replace_functions.md new file mode 120000 index 00000000000..4ec963ffd0f --- /dev/null +++ b/docs/fa/query_language/functions/string_replace_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/string_replace_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/string_search_functions.md b/docs/fa/query_language/functions/string_search_functions.md new file mode 120000 index 00000000000..0a2c7f4c4f1 --- /dev/null +++ b/docs/fa/query_language/functions/string_search_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/string_search_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/type_conversion_functions.md b/docs/fa/query_language/functions/type_conversion_functions.md new file mode 120000 index 00000000000..fcf51570d15 --- /dev/null +++ b/docs/fa/query_language/functions/type_conversion_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/type_conversion_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/url_functions.md b/docs/fa/query_language/functions/url_functions.md new file mode 120000 index 00000000000..529e4ffdd53 --- /dev/null +++ b/docs/fa/query_language/functions/url_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/url_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/functions/ym_dict_functions.md b/docs/fa/query_language/functions/ym_dict_functions.md new file mode 120000 index 00000000000..ec5ddc84479 --- /dev/null +++ b/docs/fa/query_language/functions/ym_dict_functions.md @@ -0,0 +1 @@ +../../../en/query_language/functions/ym_dict_functions.md \ No newline at end of file diff --git a/docs/fa/query_language/index.md b/docs/fa/query_language/index.md new file mode 120000 index 00000000000..44dfff9bb18 --- /dev/null +++ b/docs/fa/query_language/index.md @@ -0,0 +1 @@ +../../en/query_language/index.md \ No newline at end of file diff --git a/docs/fa/query_language/insert_into.md b/docs/fa/query_language/insert_into.md new file mode 120000 index 00000000000..29b47662b0d --- /dev/null +++ b/docs/fa/query_language/insert_into.md @@ -0,0 +1 @@ +../../en/query_language/insert_into.md \ No newline at end of file diff --git a/docs/fa/query_language/misc.md b/docs/fa/query_language/misc.md new file mode 120000 index 00000000000..3bd814f3568 --- /dev/null +++ b/docs/fa/query_language/misc.md @@ -0,0 +1 @@ +../../en/query_language/misc.md \ No newline at end of file diff --git a/docs/fa/query_language/operators.md b/docs/fa/query_language/operators.md new file mode 120000 index 00000000000..f94df928a82 --- /dev/null +++ b/docs/fa/query_language/operators.md @@ -0,0 +1 @@ +../../en/query_language/operators.md \ No newline at end of file diff --git a/docs/fa/query_language/select.md b/docs/fa/query_language/select.md new file mode 120000 index 00000000000..c8ec8369383 --- /dev/null +++ b/docs/fa/query_language/select.md @@ -0,0 +1 @@ +../../en/query_language/select.md \ No newline at end of file diff --git a/docs/fa/query_language/syntax.md b/docs/fa/query_language/syntax.md new file mode 120000 index 00000000000..5307fd51ae8 --- /dev/null +++ b/docs/fa/query_language/syntax.md @@ -0,0 +1 @@ +../../en/query_language/syntax.md \ No newline at end of file diff --git a/docs/fa/query_language/table_functions/file.md b/docs/fa/query_language/table_functions/file.md new file mode 120000 index 00000000000..a514547109a --- /dev/null +++ b/docs/fa/query_language/table_functions/file.md @@ -0,0 +1 @@ +../../../en/query_language/table_functions/file.md \ No newline at end of file diff --git a/docs/fa/query_language/table_functions/index.md b/docs/fa/query_language/table_functions/index.md new file mode 120000 index 00000000000..89b22522859 --- /dev/null +++ b/docs/fa/query_language/table_functions/index.md @@ -0,0 +1 @@ +../../../en/query_language/table_functions/index.md \ No newline at end of file diff --git a/docs/fa/query_language/table_functions/merge.md b/docs/fa/query_language/table_functions/merge.md new file mode 120000 index 00000000000..383f6c88331 --- /dev/null +++ b/docs/fa/query_language/table_functions/merge.md @@ -0,0 +1 @@ +../../../en/query_language/table_functions/merge.md \ No newline at end of file diff --git a/docs/fa/query_language/table_functions/numbers.md b/docs/fa/query_language/table_functions/numbers.md new file mode 120000 index 00000000000..a679b915669 --- /dev/null +++ b/docs/fa/query_language/table_functions/numbers.md @@ -0,0 +1 @@ +../../../en/query_language/table_functions/numbers.md \ No newline at end of file diff --git a/docs/fa/query_language/table_functions/remote.md b/docs/fa/query_language/table_functions/remote.md new file mode 120000 index 00000000000..b157c4076d3 --- /dev/null +++ b/docs/fa/query_language/table_functions/remote.md @@ -0,0 +1 @@ +../../../en/query_language/table_functions/remote.md \ No newline at end of file diff --git a/docs/fa/query_language/table_functions/url.md b/docs/fa/query_language/table_functions/url.md new file mode 120000 index 00000000000..038e08f7ba9 --- /dev/null +++ b/docs/fa/query_language/table_functions/url.md @@ -0,0 +1 @@ +../../../en/query_language/table_functions/url.md \ No newline at end of file diff --git a/docs/fa/roadmap.md b/docs/fa/roadmap.md new file mode 120000 index 00000000000..24df86352b3 --- /dev/null +++ b/docs/fa/roadmap.md @@ -0,0 +1 @@ +../en/roadmap.md \ No newline at end of file diff --git a/docs/fa/security_changelog.md b/docs/fa/security_changelog.md new file mode 120000 index 00000000000..101a4f4e48c --- /dev/null +++ b/docs/fa/security_changelog.md @@ -0,0 +1 @@ +../en/security_changelog.md \ No newline at end of file diff --git a/docs/toc_fa.yml b/docs/toc_fa.yml new file mode 100644 index 00000000000..f1d4a67ca1e --- /dev/null +++ b/docs/toc_fa.yml @@ -0,0 +1,177 @@ +nav: + +- 'Introduction': + - 'ClickHouse چیست؟': 'index.md' + - ' ویژگی های برجسته ClickHouse': 'introduction/distinctive_features.md' + - ' ویژگی های از ClickHouse که می تواند معایبی باشد': 'introduction/features_considered_disadvantages.md' + - 'Performance': 'introduction/performance.md' + - 'The Yandex.Metrica task': 'introduction/ya_metrika_task.md' + +- 'Getting started': + - ' شروع به کار': 'getting_started/index.md' + - 'Example datasets': + - 'OnTime': 'getting_started/example_datasets/ontime.md' + - ' داده های تاکسی New York': 'getting_started/example_datasets/nyc_taxi.md' + - ' بنچمارک AMPLab Big Data': 'getting_started/example_datasets/amplab_benchmark.md' + - 'WikiStat': 'getting_started/example_datasets/wikistat.md' + - ' ترابایت از لاگ های کلیک از سرویس Criteo': 'getting_started/example_datasets/criteo.md' + - ' بنچمارک Star Schema': 'getting_started/example_datasets/star_schema.md' + +- 'Interfaces': + - 'Interface ها': 'interfaces/index.md' + - ' کلاینت Command-line': 'interfaces/cli.md' + - 'HTTP interface': 'interfaces/http_interface.md' + - ' درایور JDBC': 'interfaces/jdbc.md' + - 'Native interface (TCP)': 'interfaces/tcp.md' + - ' کتابخانه های توسعه دهندگان third-party': 'interfaces/third-party_client_libraries.md' + - 'interface های visual توسعه دهندگان third-party': 'interfaces/third-party_gui.md' + - ' فرمت های Input و Output': 'interfaces/formats.md' + +- 'Data types': + - 'Introduction': 'data_types/index.md' + - 'UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64': 'data_types/int_uint.md' + - 'Float32, Float64': 'data_types/float.md' + - ' مقادیر Boolean': 'data_types/boolean.md' + - 'String': 'data_types/string.md' + - 'FixedString(N)': 'data_types/fixedstring.md' + - 'Date': 'data_types/date.md' + - 'DateTime': 'data_types/datetime.md' + - 'Enum': 'data_types/enum.md' + - 'Array(T)': 'data_types/array.md' + - 'AggregateFunction(name, types_of_arguments...)': 'data_types/nested_data_structures/aggregatefunction.md' + - 'Tuple(T1, T2, ...)': 'data_types/tuple.md' + - 'Nested data structures': + - 'hidden': 'data_types/nested_data_structures/index.md' + - 'Nested(Name1 Type1, Name2 Type2, ...)': 'data_types/nested_data_structures/nested.md' + - 'Special data types': + - 'hidden': 'data_types/special_data_types/index.md' + - 'Expression': 'data_types/special_data_types/expression.md' + - 'Set': 'data_types/special_data_types/set.md' + +- 'SQL reference': + - 'hidden': 'query_language/index.md' + - 'SELECT': 'query_language/select.md' + - 'INSERT INTO': 'query_language/insert_into.md' + - 'CREATE': 'query_language/create.md' + - 'ALTER': 'query_language/alter.md' + - 'Other kinds of queries': 'query_language/misc.md' + - 'Functions': + - 'Introduction': 'query_language/functions/index.md' + - 'Arithmetic functions': 'query_language/functions/arithmetic_functions.md' + - 'Comparison functions': 'query_language/functions/comparison_functions.md' + - 'Logical functions': 'query_language/functions/logical_functions.md' + - 'Type conversion functions': 'query_language/functions/type_conversion_functions.md' + - 'Functions for working with dates and times': 'query_language/functions/date_time_functions.md' + - 'Functions for working with strings': 'query_language/functions/string_functions.md' + - 'Functions for searching strings': 'query_language/functions/string_search_functions.md' + - 'Functions for searching and replacing in strings': 'query_language/functions/string_replace_functions.md' + - 'Conditional functions': 'query_language/functions/conditional_functions.md' + - 'Mathematical functions': 'query_language/functions/math_functions.md' + - 'Rounding functions': 'query_language/functions/rounding_functions.md' + - 'Functions for working with arrays': 'query_language/functions/array_functions.md' + - 'Functions for splitting and merging strings and arrays': 'query_language/functions/splitting_merging_functions.md' + - 'Bit functions': 'query_language/functions/bit_functions.md' + - 'Hash functions': 'query_language/functions/hash_functions.md' + - 'Functions for generating pseudo-random numbers': 'query_language/functions/random_functions.md' + - 'Encoding functions': 'query_language/functions/encoding_functions.md' + - 'Functions for working with URLs': 'query_language/functions/url_functions.md' + - 'Functions for working with IP addresses': 'query_language/functions/ip_address_functions.md' + - 'Functions for working with JSON.': 'query_language/functions/json_functions.md' + - 'Higher-order functions': 'query_language/functions/higher_order_functions.md' + - 'Other functions': 'query_language/functions/other_functions.md' + - 'Functions for working with external dictionaries': 'query_language/functions/ext_dict_functions.md' + - 'Functions for working with Yandex.Metrica dictionaries': 'query_language/functions/ym_dict_functions.md' + - 'Functions for implementing the IN operator': 'query_language/functions/in_functions.md' + - 'arrayJoin function': 'query_language/functions/array_join.md' + - 'Aggregate functions': + - 'Introduction': 'query_language/agg_functions/index.md' + - 'Function reference': 'query_language/agg_functions/reference.md' + - 'Aggregate function combinators': 'query_language/agg_functions/combinators.md' + - 'Parametric aggregate functions': 'query_language/agg_functions/parametric_functions.md' + - 'Table functions': + - 'Introduction': 'query_language/table_functions/index.md' + - 'file': 'query_language/table_functions/file.md' + - 'merge': 'query_language/table_functions/merge.md' + - 'numbers': 'query_language/table_functions/numbers.md' + - 'remote': 'query_language/table_functions/remote.md' + - 'url': 'query_language/table_functions/url.md' + - 'Dictionaries': + - 'Introduction': 'query_language/dicts/index.md' + - 'External dictionaries': + - 'General description': 'query_language/dicts/external_dicts.md' + - 'Configuring an external dictionary': 'query_language/dicts/external_dicts_dict.md' + - 'Storing dictionaries in memory': 'query_language/dicts/external_dicts_dict_layout.md' + - 'Dictionary updates': 'query_language/dicts/external_dicts_dict_lifetime.md' + - 'Sources of external dictionaries': 'query_language/dicts/external_dicts_dict_sources.md' + - 'Dictionary key and fields': 'query_language/dicts/external_dicts_dict_structure.md' + - 'Internal dictionaries': 'query_language/dicts/internal_dicts.md' + - 'Operators': 'query_language/operators.md' + - 'General syntax': 'query_language/syntax.md' + +- 'Operations': + - 'hidden': 'operations/index.md' + - 'Table engines': + - 'Introduction': 'operations/table_engines/index.md' + - 'MergeTree family': + - 'MergeTree': 'operations/table_engines/mergetree.md' + - 'Data replication': 'operations/table_engines/replication.md' + - 'Custom partitioning key': 'operations/table_engines/custom_partitioning_key.md' + - 'ReplacingMergeTree': 'operations/table_engines/replacingmergetree.md' + - 'SummingMergeTree': 'operations/table_engines/summingmergetree.md' + - 'AggregatingMergeTree': 'operations/table_engines/aggregatingmergetree.md' + - 'CollapsingMergeTree': 'operations/table_engines/collapsingmergetree.md' + - 'GraphiteMergeTree': 'operations/table_engines/graphitemergetree.md' + - 'For small data': + - 'TinyLog': 'operations/table_engines/tinylog.md' + - 'Log': 'operations/table_engines/log.md' + - 'Memory': 'operations/table_engines/memory.md' + - 'Buffer': 'operations/table_engines/buffer.md' + - 'External data': 'operations/table_engines/external_data.md' + - 'Special': + - 'Distributed': 'operations/table_engines/distributed.md' + - 'Dictionary': 'operations/table_engines/dictionary.md' + - 'Merge': 'operations/table_engines/merge.md' + - 'File': 'operations/table_engines/file.md' + - 'Null': 'operations/table_engines/null.md' + - 'Set': 'operations/table_engines/set.md' + - 'Join': 'operations/table_engines/join.md' + - 'URL': 'operations/table_engines/url.md' + - 'View': 'operations/table_engines/view.md' + - 'MaterializedView': 'operations/table_engines/materializedview.md' + - 'Integrations': + - 'Kafka': 'operations/table_engines/kafka.md' + - 'MySQL': 'operations/table_engines/mysql.md' + - 'Access rights': 'operations/access_rights.md' + - 'Configuration files': 'operations/configuration_files.md' + - 'Quotas': 'operations/quotas.md' + - 'System tables': 'operations/system_tables.md' + - 'Usage recommendations': 'operations/tips.md' + - 'Server configuration parameters': + - 'Introduction': 'operations/server_settings/index.md' + - 'Server settings': 'operations/server_settings/settings.md' + - 'Settings': + - 'Introduction': 'operations/settings/index.md' + - 'Restrictions on query complexity': 'operations/settings/query_complexity.md' + - 'Settings': 'operations/settings/settings.md' + - 'Settings profiles': 'operations/settings/settings_profiles.md' + + - 'Utilities': + - 'Overview': 'operations/utils/index.md' + - 'clickhouse-copier': 'operations/utils/clickhouse-copier.md' + - 'clickhouse-local': 'operations/utils/clickhouse-local.md' + +- 'F.A.Q.': + - 'General questions': 'faq/general.md' + +- 'Development': + - 'hidden': 'development/index.md' + - 'Overview of ClickHouse architecture': 'development/architecture.md' + - 'How to build ClickHouse on Linux': 'development/build.md' + - 'How to build ClickHouse on Mac OS X': 'development/build_osx.md' + - 'How to write C++ code': 'development/style.md' + - 'How to run ClickHouse tests': 'development/tests.md' + +- 'What''s new': + - 'Roadmap': 'roadmap.md' + - 'Changelog': 'changelog.md' + - 'Security changelog': 'security_changelog.md' diff --git a/docs/tools/build.py b/docs/tools/build.py index deac2adb85a..f5e94e2e076 100755 --- a/docs/tools/build.py +++ b/docs/tools/build.py @@ -17,7 +17,6 @@ from mkdocs.commands import build as mkdocs_build from concatenate import concatenate - @contextlib.contextmanager def temp_dir(): path = tempfile.mkdtemp(dir=os.environ.get('TEMP')) @@ -46,6 +45,7 @@ def build_for_lang(lang, args): 'name': 'mkdocs', 'custom_dir': os.path.join(os.path.dirname(__file__), args.theme_dir), 'language': lang, + 'direction': 'rtl' if lang == 'fa' else 'ltr', 'feature': { 'tabs': False }, @@ -69,7 +69,7 @@ def build_for_lang(lang, args): cfg = config.load_config( config_file=config_path, - site_name='ClickHouse Documentation' if lang == 'en' else 'Документация ClickHouse', + site_name='ClickHouse Documentation' if lang == 'en' or 'fa' else 'Документация ClickHouse', docs_dir=os.path.join(args.docs_dir, lang), site_dir=os.path.join(args.output_dir, lang), strict=True, @@ -83,11 +83,12 @@ def build_for_lang(lang, args): markdown_extensions=[ 'admonition', 'attr_list', - 'codehilite' + 'codehilite', + 'extra' ], plugins=[{ 'search': { - 'lang': ['en'] if lang == 'en' else ['en', lang] + 'lang': ['en'] if lang == 'en' or 'fa' else ['en', lang] } }], extra={ @@ -169,7 +170,7 @@ def build(args): if __name__ == '__main__': arg_parser = argparse.ArgumentParser() - arg_parser.add_argument('--lang', default='en,ru') + arg_parser.add_argument('--lang', default='en,ru,fa') arg_parser.add_argument('--docs-dir', default='.') arg_parser.add_argument('--theme-dir', default='mkdocs-material-theme') arg_parser.add_argument('--output-dir', default='build') diff --git a/docs/tools/mkdocs-material-theme/assets/stylesheets/application.2a88008a.css b/docs/tools/mkdocs-material-theme/assets/stylesheets/application.2a88008a.css new file mode 100644 index 00000000000..8a33734833a --- /dev/null +++ b/docs/tools/mkdocs-material-theme/assets/stylesheets/application.2a88008a.css @@ -0,0 +1,2 @@ +html{-webkit-box-sizing:border-box;box-sizing:border-box}*,:after,:before{-webkit-box-sizing:inherit;box-sizing:inherit}html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none}body{margin:0}hr{overflow:visible;-webkit-box-sizing:content-box;box-sizing:content-box}a{-webkit-text-decoration-skip:objects}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}small,sub,sup{font-size:80%}sub,sup{position:relative;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}table{border-collapse:collapse;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{padding:0;border:0;outline-style:none;background:transparent;font-size:inherit}input{border:0;outline:0}.md-clipboard:before,.md-icon,.md-nav__button,.md-nav__link:after,.md-nav__title:before,.md-search-result__article--document:before,.md-source-file:before,.md-typeset .admonition>.admonition-title:before,.md-typeset .admonition>summary:before,.md-typeset .critic.comment:before,.md-typeset .footnote-backref,.md-typeset .task-list-control .task-list-indicator:before,.md-typeset details>.admonition-title:before,.md-typeset details>summary:before,.md-typeset summary:after{font-family:Material Icons;font-style:normal;font-variant:normal;font-weight:400;line-height:1;text-transform:none;white-space:nowrap;speak:none;word-wrap:normal;direction:ltr}.md-content__icon,.md-footer-nav__button,.md-header-nav__button,.md-nav__button,.md-nav__title:before,.md-search-result__article--document:before{display:inline-block;margin:.4rem;padding:.8rem;font-size:2.4rem;cursor:pointer}.md-icon--arrow-back:before{content:"\E5C4"}.md-icon--arrow-forward:before{content:"\E5C8"}.md-icon--menu:before{content:"\E5D2"}.md-icon--search:before{content:"\E8B6"}[dir=rtl] .md-icon--arrow-back:before{content:"\E5C8"}[dir=rtl] .md-icon--arrow-forward:before{content:"\E5C4"}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,input{color:rgba(0,0,0,.87);-webkit-font-feature-settings:"kern","liga";font-feature-settings:"kern","liga";font-family:Helvetica Neue,Helvetica,Arial,sans-serif}code,kbd,pre{color:rgba(0,0,0,.87);-webkit-font-feature-settings:"kern";font-feature-settings:"kern";font-family:Courier New,Courier,monospace}.md-typeset{font-size:1.6rem;line-height:1.6;-webkit-print-color-adjust:exact}.md-typeset blockquote,.md-typeset ol,.md-typeset p,.md-typeset ul{margin:1em 0}.md-typeset h1{margin:0 0 4rem;color:rgba(0,0,0,.54);font-size:3.125rem;line-height:1.3}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{margin:4rem 0 1.6rem;font-size:2.5rem;line-height:1.4}.md-typeset h3{margin:3.2rem 0 1.6rem;font-size:2rem;font-weight:400;letter-spacing:-.01em;line-height:1.5}.md-typeset h2+h3{margin-top:1.6rem}.md-typeset h4{font-size:1.6rem}.md-typeset h4,.md-typeset h5,.md-typeset h6{margin:1.6rem 0;font-weight:700;letter-spacing:-.01em}.md-typeset h5,.md-typeset h6{color:rgba(0,0,0,.54);font-size:1.28rem}.md-typeset h5{text-transform:uppercase}.md-typeset hr{margin:1.5em 0;border-bottom:.1rem dotted rgba(0,0,0,.26)}.md-typeset a{color:#3f51b5;word-break:break-word}.md-typeset a,.md-typeset a:before{-webkit-transition:color .125s;transition:color .125s}.md-typeset a:active,.md-typeset a:hover{color:#536dfe}.md-typeset code,.md-typeset pre{background-color:hsla(0,0%,93%,.5);color:#37474f;font-size:85%;direction:ltr}.md-typeset code{margin:0 .29412em;padding:.07353em 0;border-radius:.2rem;-webkit-box-shadow:.29412em 0 0 hsla(0,0%,93%,.5),-.29412em 0 0 hsla(0,0%,93%,.5);box-shadow:.29412em 0 0 hsla(0,0%,93%,.5),-.29412em 0 0 hsla(0,0%,93%,.5);word-break:break-word;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset h1 code,.md-typeset h2 code,.md-typeset h3 code,.md-typeset h4 code,.md-typeset h5 code,.md-typeset h6 code{margin:0;background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.md-typeset a>code{margin:inherit;padding:inherit;border-radius:none;background-color:inherit;color:inherit;-webkit-box-shadow:none;box-shadow:none}.md-typeset pre{position:relative;margin:1em 0;border-radius:.2rem;line-height:1.4;-webkit-overflow-scrolling:touch}.md-typeset pre>code{display:block;margin:0;padding:1.05rem 1.2rem;background-color:transparent;font-size:inherit;-webkit-box-shadow:none;box-shadow:none;-webkit-box-decoration-break:none;box-decoration-break:none;overflow:auto}.md-typeset pre>code::-webkit-scrollbar{width:.4rem;height:.4rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-typeset kbd{padding:0 .29412em;border:.1rem solid #c9c9c9;border-radius:.3rem;border-bottom-color:#bcbcbc;background-color:#fcfcfc;color:#555;font-size:85%;-webkit-box-shadow:0 .1rem 0 #b0b0b0;box-shadow:0 .1rem 0 #b0b0b0;word-break:break-word}.md-typeset mark{margin:0 .25em;padding:.0625em 0;border-radius:.2rem;background-color:rgba(255,235,59,.5);-webkit-box-shadow:.25em 0 0 rgba(255,235,59,.5),-.25em 0 0 rgba(255,235,59,.5);box-shadow:.25em 0 0 rgba(255,235,59,.5),-.25em 0 0 rgba(255,235,59,.5);word-break:break-word;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset abbr{border-bottom:.1rem dotted rgba(0,0,0,.54);text-decoration:none;cursor:help}.md-typeset small{opacity:.75}.md-typeset sub,.md-typeset sup{margin-left:.07812em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.07812em;margin-left:0}.md-typeset blockquote{padding-left:1.2rem;border-left:.4rem solid rgba(0,0,0,.26);color:rgba(0,0,0,.54)}[dir=rtl] .md-typeset blockquote{padding-right:1.2rem;padding-left:0;border-right:.4rem solid rgba(0,0,0,.26);border-left:initial}.md-typeset ul{list-style-type:disc}.md-typeset ol,.md-typeset ul{margin-left:.625em;padding:0}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em;margin-left:0}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em;margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em;margin-left:0}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin:.5em 0 .5em .625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em;margin-left:0}.md-typeset dd{margin:1em 0 1em 1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em;margin-left:0}.md-typeset iframe,.md-typeset img,.md-typeset svg{max-width:100%}.md-typeset table:not([class]){-webkit-box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);display:inline-block;max-width:100%;border-radius:.2rem;font-size:1.28rem;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{min-width:10rem;padding:1.2rem 1.6rem;background-color:rgba(0,0,0,.54);color:#fff;vertical-align:top}.md-typeset table:not([class]) td{padding:1.2rem 1.6rem;border-top:.1rem solid rgba(0,0,0,.07);vertical-align:top}.md-typeset table:not([class]) tr:first-child td{border-top:0}.md-typeset table:not([class]) a{word-break:normal}.md-typeset__scrollwrap{margin:1em -1.6rem;overflow-x:auto;-webkit-overflow-scrolling:touch}.md-typeset .md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 1.6rem}.md-typeset .md-typeset__table table{display:table;width:100%;margin:0;overflow:hidden}html{font-size:62.5%;overflow-x:hidden}body,html{height:100%}body{position:relative}hr{display:block;height:.1rem;padding:0;border:0}.md-svg{display:none}.md-grid{max-width:122rem;margin-right:auto;margin-left:auto}.md-container,.md-main{overflow:auto}.md-container{display:table;width:100%;height:100%;padding-top:4.8rem;table-layout:fixed}.md-main{display:table-row;height:100%}.md-main__inner{height:100%;padding-top:3rem;padding-bottom:.1rem}.md-toggle{display:none}.md-overlay{position:fixed;top:0;width:0;height:0;-webkit-transition:width 0s .25s,height 0s .25s,opacity .25s;transition:width 0s .25s,height 0s .25s,opacity .25s;background-color:rgba(0,0,0,.54);opacity:0;z-index:3}.md-flex{display:table}.md-flex__cell{display:table-cell;position:relative;vertical-align:top}.md-flex__cell--shrink{width:0}.md-flex__cell--stretch{display:table;width:100%;table-layout:fixed}.md-flex__ellipsis{display:table-cell;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}@page{margin:25mm}.md-clipboard{position:absolute;top:.6rem;right:.6rem;width:2.8rem;height:2.8rem;border-radius:.2rem;font-size:1.6rem;cursor:pointer;z-index:1;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-clipboard:before{-webkit-transition:color .25s,opacity .25s;transition:color .25s,opacity .25s;color:rgba(0,0,0,.07);content:"\E14D"}.codehilite:hover .md-clipboard:before,.md-typeset .highlight:hover .md-clipboard:before,pre:hover .md-clipboard:before{color:rgba(0,0,0,.54)}.md-clipboard:focus:before,.md-clipboard:hover:before{color:#536dfe}.md-clipboard__message{display:block;position:absolute;top:0;right:3.4rem;padding:.6rem 1rem;-webkit-transform:translateX(.8rem);transform:translateX(.8rem);-webkit-transition:opacity .175s,-webkit-transform .25s cubic-bezier(.9,.1,.9,0);transition:opacity .175s,-webkit-transform .25s cubic-bezier(.9,.1,.9,0);transition:transform .25s cubic-bezier(.9,.1,.9,0),opacity .175s;transition:transform .25s cubic-bezier(.9,.1,.9,0),opacity .175s,-webkit-transform .25s cubic-bezier(.9,.1,.9,0);border-radius:.2rem;background-color:rgba(0,0,0,.54);color:#fff;font-size:1.28rem;white-space:nowrap;opacity:0;pointer-events:none}.md-clipboard__message--active{-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1;pointer-events:auto}.md-clipboard__message:before{content:attr(aria-label)}.md-clipboard__message:after{display:block;position:absolute;top:50%;right:-.4rem;width:0;margin-top:-.4rem;border-width:.4rem 0 .4rem .4rem;border-style:solid;border-color:transparent rgba(0,0,0,.54);content:""}.md-content__inner{margin:0 1.6rem 2.4rem;padding-top:1.2rem}.md-content__inner:before{display:block;height:.8rem;content:""}.md-content__inner>:last-child{margin-bottom:0}.md-content__icon{position:relative;margin:.8rem 0;padding:0;float:right}.md-typeset .md-content__icon{color:rgba(0,0,0,.26)}.md-header{position:fixed;top:0;right:0;left:0;height:4.8rem;-webkit-transition:background-color .25s,color .25s;transition:background-color .25s,color .25s;background-color:#3f51b5;color:#fff;z-index:2;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-header,.no-js .md-header{-webkit-box-shadow:none;box-shadow:none}.no-js .md-header{-webkit-transition:none;transition:none}.md-header[data-md-state=shadow]{-webkit-transition:background-color .25s,color .25s,-webkit-box-shadow .25s;transition:background-color .25s,color .25s,-webkit-box-shadow .25s;transition:background-color .25s,color .25s,box-shadow .25s;transition:background-color .25s,color .25s,box-shadow .25s,-webkit-box-shadow .25s;-webkit-box-shadow:0 0 .4rem rgba(0,0,0,.1),0 .4rem .8rem rgba(0,0,0,.2);box-shadow:0 0 .4rem rgba(0,0,0,.1),0 .4rem .8rem rgba(0,0,0,.2)}.md-header-nav{padding:0 .4rem}.md-header-nav__button{position:relative;-webkit-transition:opacity .25s;transition:opacity .25s;z-index:1}.md-header-nav__button:hover{opacity:.7}.md-header-nav__button.md-logo *{display:block}.no-js .md-header-nav__button.md-icon--search{display:none}.md-header-nav__topic{display:block;position:absolute;-webkit-transition:opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.md-header-nav__topic+.md-header-nav__topic{-webkit-transform:translateX(2.5rem);transform:translateX(2.5rem);-webkit-transition:opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);transition:opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);opacity:0;z-index:-1;pointer-events:none}[dir=rtl] .md-header-nav__topic+.md-header-nav__topic{-webkit-transform:translateX(-2.5rem);transform:translateX(-2.5rem)}.no-js .md-header-nav__topic{position:static}.no-js .md-header-nav__topic+.md-header-nav__topic{display:none}.md-header-nav__title{padding:0 2rem;font-size:1.8rem;line-height:4.8rem}.md-header-nav__title[data-md-state=active] .md-header-nav__topic{-webkit-transform:translateX(-2.5rem);transform:translateX(-2.5rem);-webkit-transition:opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);transition:opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);opacity:0;z-index:-1;pointer-events:none}[dir=rtl] .md-header-nav__title[data-md-state=active] .md-header-nav__topic{-webkit-transform:translateX(2.5rem);transform:translateX(2.5rem)}.md-header-nav__title[data-md-state=active] .md-header-nav__topic+.md-header-nav__topic{-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);opacity:1;z-index:0;pointer-events:auto}.md-header-nav__source{display:none}.md-hero{-webkit-transition:background .25s;transition:background .25s;background-color:#3f51b5;color:#fff;font-size:2rem;overflow:hidden}.md-hero__inner{margin-top:2rem;padding:1.6rem 1.6rem .8rem;-webkit-transition:opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);-webkit-transition-delay:.1s;transition-delay:.1s}[data-md-state=hidden] .md-hero__inner{pointer-events:none;-webkit-transform:translateY(1.25rem);transform:translateY(1.25rem);-webkit-transition:opacity .1s 0s,-webkit-transform 0s .4s;transition:opacity .1s 0s,-webkit-transform 0s .4s;transition:transform 0s .4s,opacity .1s 0s;transition:transform 0s .4s,opacity .1s 0s,-webkit-transform 0s .4s;opacity:0}.md-hero--expand .md-hero__inner{margin-bottom:2.4rem}.md-footer-nav{background-color:rgba(0,0,0,.87);color:#fff}.md-footer-nav__inner{padding:.4rem;overflow:auto}.md-footer-nav__link{padding-top:2.8rem;padding-bottom:.8rem;-webkit-transition:opacity .25s;transition:opacity .25s}.md-footer-nav__link:hover{opacity:.7}.md-footer-nav__link--prev{width:25%;float:left}[dir=rtl] .md-footer-nav__link--prev{float:right}.md-footer-nav__link--next{width:75%;float:right;text-align:right}[dir=rtl] .md-footer-nav__link--next{float:left;text-align:left}.md-footer-nav__button{-webkit-transition:background .25s;transition:background .25s}.md-footer-nav__title{position:relative;padding:0 2rem;font-size:1.8rem;line-height:4.8rem}.md-footer-nav__direction{position:absolute;right:0;left:0;margin-top:-2rem;padding:0 2rem;color:hsla(0,0%,100%,.7);font-size:1.5rem}.md-footer-meta{background-color:rgba(0,0,0,.895)}.md-footer-meta__inner{padding:.4rem;overflow:auto}html .md-footer-meta.md-typeset a{color:hsla(0,0%,100%,.7)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:#fff}.md-footer-copyright{margin:0 1.2rem;padding:.8rem 0;color:hsla(0,0%,100%,.3);font-size:1.28rem}.md-footer-copyright__highlight{color:hsla(0,0%,100%,.7)}.md-footer-social{margin:0 .8rem;padding:.4rem 0 1.2rem}.md-footer-social__link{display:inline-block;width:3.2rem;height:3.2rem;font-size:1.6rem;text-align:center}.md-footer-social__link:before{line-height:1.9}.md-nav{font-size:1.4rem;line-height:1.3}.md-nav--secondary .md-nav__link--active{color:#3f51b5}.md-nav__title{display:block;padding:0 1.2rem;font-weight:700;text-overflow:ellipsis;overflow:hidden}.md-nav__title:before{display:none;content:"\E5C4"}[dir=rtl] .md-nav__title:before{content:"\E5C8"}.md-nav__title .md-nav__button{display:none}.md-nav__list{margin:0;padding:0;list-style:none}.md-nav__item{padding:0 1.2rem}.md-nav__item:last-child{padding-bottom:1.2rem}.md-nav__item .md-nav__item{padding-right:0}[dir=rtl] .md-nav__item .md-nav__item{padding-right:1.2rem;padding-left:0}.md-nav__item .md-nav__item:last-child{padding-bottom:0}.md-nav__button img{width:100%;height:auto}.md-nav__link{display:block;margin-top:.625em;-webkit-transition:color .125s;transition:color .125s;text-overflow:ellipsis;cursor:pointer;overflow:hidden}.md-nav__item--nested>.md-nav__link:after{content:"\E313"}html .md-nav__link[for=toc],html .md-nav__link[for=toc]+.md-nav__link:after,html .md-nav__link[for=toc]~.md-nav{display:none}.md-nav__link[data-md-state=blur]{color:rgba(0,0,0,.54)}.md-nav__link:active{color:#3f51b5}.md-nav__item--nested>.md-nav__link{color:inherit}.md-nav__link:focus,.md-nav__link:hover{color:#536dfe}.md-nav__source,.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}.md-search__form{position:relative}.md-search__input{position:relative;padding:0 4.4rem 0 7.2rem;text-overflow:ellipsis;z-index:2}[dir=rtl] .md-search__input{padding:0 7.2rem 0 4.4rem}.md-search__input::-webkit-input-placeholder{-webkit-transition:color .25s cubic-bezier(.1,.7,.1,1);transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input:-ms-input-placeholder,.md-search__input::-ms-input-placeholder{-webkit-transition:color .25s cubic-bezier(.1,.7,.1,1);transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::placeholder{-webkit-transition:color .25s cubic-bezier(.1,.7,.1,1);transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::-webkit-input-placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input:-ms-input-placeholder,.md-search__input::-ms-input-placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input::-ms-clear{display:none}.md-search__icon{position:absolute;-webkit-transition:color .25s cubic-bezier(.1,.7,.1,1),opacity .25s;transition:color .25s cubic-bezier(.1,.7,.1,1),opacity .25s;font-size:2.4rem;cursor:pointer;z-index:2}.md-search__icon:hover{opacity:.7}.md-search__icon[for=search]{top:.6rem;left:1rem}[dir=rtl] .md-search__icon[for=search]{right:1rem;left:auto}.md-search__icon[for=search]:before{content:"\E8B6"}.md-search__icon[type=reset]{top:.6rem;right:1rem;-webkit-transform:scale(.125);transform:scale(.125);-webkit-transition:opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);transition:opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);opacity:0}[dir=rtl] .md-search__icon[type=reset]{right:auto;left:1rem}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]{-webkit-transform:scale(1);transform:scale(1);opacity:1}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]:hover{opacity:.7}.md-search__output{position:absolute;width:100%;border-radius:0 0 .2rem .2rem;overflow:hidden;z-index:1}.md-search__scrollwrap{height:100%;background-color:#fff;-webkit-box-shadow:0 .1rem 0 rgba(0,0,0,.07) inset;box-shadow:inset 0 .1rem 0 rgba(0,0,0,.07);overflow-y:auto;-webkit-overflow-scrolling:touch}.md-search-result{color:rgba(0,0,0,.87);word-break:break-word}.md-search-result__meta{padding:0 1.6rem;background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.54);font-size:1.28rem;line-height:3.6rem}.md-search-result__list{margin:0;padding:0;border-top:.1rem solid rgba(0,0,0,.07);list-style:none}.md-search-result__item{-webkit-box-shadow:0 -.1rem 0 rgba(0,0,0,.07);box-shadow:0 -.1rem 0 rgba(0,0,0,.07)}.md-search-result__link{display:block;-webkit-transition:background .25s;transition:background .25s;outline:0;overflow:hidden}.md-search-result__link:hover,.md-search-result__link[data-md-state=active]{background-color:rgba(83,109,254,.1)}.md-search-result__link:hover .md-search-result__article:before,.md-search-result__link[data-md-state=active] .md-search-result__article:before{opacity:.7}.md-search-result__link:last-child .md-search-result__teaser{margin-bottom:1.2rem}.md-search-result__article{position:relative;padding:0 1.6rem;overflow:auto}.md-search-result__article--document:before{position:absolute;left:0;margin:.2rem;-webkit-transition:opacity .25s;transition:opacity .25s;color:rgba(0,0,0,.54);content:"\E880"}[dir=rtl] .md-search-result__article--document:before{right:0;left:auto}.md-search-result__article--document .md-search-result__title{margin:1.1rem 0;font-size:1.6rem;font-weight:400;line-height:1.4}.md-search-result__title{margin:.5em 0;font-size:1.28rem;font-weight:700;line-height:1.4}.md-search-result__teaser{display:-webkit-box;max-height:3.3rem;margin:.5em 0;color:rgba(0,0,0,.54);font-size:1.28rem;line-height:1.4;text-overflow:ellipsis;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:2}.md-search-result em{font-style:normal;font-weight:700;text-decoration:underline}.md-sidebar{position:absolute;width:24.2rem;padding:2.4rem 0;overflow:hidden}.md-sidebar[data-md-state=lock]{position:fixed;top:4.8rem}.md-sidebar--secondary{display:none}.md-sidebar__scrollwrap{max-height:100%;margin:0 .4rem;overflow-y:auto;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-sidebar__scrollwrap::-webkit-scrollbar{width:.4rem;height:.4rem}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}@-webkit-keyframes md-source__facts--done{0%{height:0}to{height:1.3rem}}@keyframes md-source__facts--done{0%{height:0}to{height:1.3rem}}@-webkit-keyframes md-source__fact--done{0%{-webkit-transform:translateY(100%);transform:translateY(100%);opacity:0}50%{opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes md-source__fact--done{0%{-webkit-transform:translateY(100%);transform:translateY(100%);opacity:0}50%{opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}.md-source{display:block;padding-right:1.2rem;-webkit-transition:opacity .25s;transition:opacity .25s;font-size:1.3rem;line-height:1.2;white-space:nowrap}[dir=rtl] .md-source{padding-right:0;padding-left:1.2rem}.md-source:hover{opacity:.7}.md-source:after,.md-source__icon{display:inline-block;height:4.8rem;content:"";vertical-align:middle}.md-source__icon{width:4.8rem}.md-source__icon svg{width:2.4rem;height:2.4rem;margin-top:1.2rem;margin-left:1.2rem}[dir=rtl] .md-source__icon svg{margin-right:1.2rem;margin-left:0}.md-source__icon+.md-source__repository{margin-left:-4.4rem;padding-left:4rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-4.4rem;margin-left:0;padding-right:4rem;padding-left:0}.md-source__repository{display:inline-block;max-width:100%;margin-left:1.2rem;font-weight:700;text-overflow:ellipsis;overflow:hidden;vertical-align:middle}.md-source__facts{margin:0;padding:0;font-size:1.1rem;font-weight:700;list-style-type:none;opacity:.75;overflow:hidden}[data-md-state=done] .md-source__facts{-webkit-animation:md-source__facts--done .25s ease-in;animation:md-source__facts--done .25s ease-in}.md-source__fact{float:left}[dir=rtl] .md-source__fact{float:right}[data-md-state=done] .md-source__fact{-webkit-animation:md-source__fact--done .4s ease-out;animation:md-source__fact--done .4s ease-out}.md-source__fact:before{margin:0 .2rem;content:"\B7"}.md-source__fact:first-child:before{display:none}.md-source-file{display:inline-block;margin:1em .5em 1em 0;padding-right:.5rem;border-radius:.2rem;background-color:rgba(0,0,0,.07);font-size:1.28rem;list-style-type:none;cursor:pointer;overflow:hidden}.md-source-file:before{display:inline-block;margin-right:.5rem;padding:.5rem;background-color:rgba(0,0,0,.26);color:#fff;font-size:1.6rem;content:"\E86F";vertical-align:middle}html .md-source-file{-webkit-transition:background .4s,color .4s,-webkit-box-shadow .4s cubic-bezier(.4,0,.2,1);transition:background .4s,color .4s,-webkit-box-shadow .4s cubic-bezier(.4,0,.2,1);transition:background .4s,color .4s,box-shadow .4s cubic-bezier(.4,0,.2,1);transition:background .4s,color .4s,box-shadow .4s cubic-bezier(.4,0,.2,1),-webkit-box-shadow .4s cubic-bezier(.4,0,.2,1)}html .md-source-file:before{-webkit-transition:inherit;transition:inherit}html body .md-typeset .md-source-file{color:rgba(0,0,0,.54)}.md-source-file:hover{-webkit-box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36);box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36)}.md-source-file:hover:before{background-color:#536dfe}.md-tabs{width:100%;-webkit-transition:background .25s;transition:background .25s;background-color:#3f51b5;color:#fff;overflow:auto}.md-tabs__list{margin:0;margin-left:.4rem;padding:0;list-style:none;white-space:nowrap}.md-tabs__item{display:inline-block;height:4.8rem;padding-right:1.2rem;padding-left:1.2rem}.md-tabs__link{display:block;margin-top:1.6rem;-webkit-transition:opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);font-size:1.4rem;opacity:.7}.md-tabs__link--active,.md-tabs__link:hover{color:inherit;opacity:1}.md-tabs__item:nth-child(2) .md-tabs__link{-webkit-transition-delay:.02s;transition-delay:.02s}.md-tabs__item:nth-child(3) .md-tabs__link{-webkit-transition-delay:.04s;transition-delay:.04s}.md-tabs__item:nth-child(4) .md-tabs__link{-webkit-transition-delay:.06s;transition-delay:.06s}.md-tabs__item:nth-child(5) .md-tabs__link{-webkit-transition-delay:.08s;transition-delay:.08s}.md-tabs__item:nth-child(6) .md-tabs__link{-webkit-transition-delay:.1s;transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{-webkit-transition-delay:.12s;transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{-webkit-transition-delay:.14s;transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{-webkit-transition-delay:.16s;transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{-webkit-transition-delay:.18s;transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{-webkit-transition-delay:.2s;transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{-webkit-transition-delay:.22s;transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{-webkit-transition-delay:.24s;transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{-webkit-transition-delay:.26s;transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{-webkit-transition-delay:.28s;transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{-webkit-transition-delay:.3s;transition-delay:.3s}.md-tabs[data-md-state=hidden]{pointer-events:none}.md-tabs[data-md-state=hidden] .md-tabs__link{-webkit-transform:translateY(50%);transform:translateY(50%);-webkit-transition:color .25s,opacity .1s,-webkit-transform 0s .4s;transition:color .25s,opacity .1s,-webkit-transform 0s .4s;transition:color .25s,transform 0s .4s,opacity .1s;transition:color .25s,transform 0s .4s,opacity .1s,-webkit-transform 0s .4s;opacity:0}.md-typeset .admonition,.md-typeset details{-webkit-box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);position:relative;margin:1.5625em 0;padding:0 1.2rem;border-left:.4rem solid #448aff;border-radius:.2rem;font-size:1.28rem;overflow:auto}[dir=rtl] .md-typeset .admonition,[dir=rtl] .md-typeset details{border-right:.4rem solid #448aff;border-left:none}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:1.2rem}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin:1em 0}.md-typeset .admonition>.admonition-title,.md-typeset .admonition>summary,.md-typeset details>.admonition-title,.md-typeset details>summary{margin:0 -1.2rem;padding:.8rem 1.2rem .8rem 4rem;border-bottom:.1rem solid rgba(68,138,255,.1);background-color:rgba(68,138,255,.1);font-weight:700}[dir=rtl] .md-typeset .admonition>.admonition-title,[dir=rtl] .md-typeset .admonition>summary,[dir=rtl] .md-typeset details>.admonition-title,[dir=rtl] .md-typeset details>summary{padding:.8rem 4rem .8rem 1.2rem}.md-typeset .admonition>.admonition-title:last-child,.md-typeset .admonition>summary:last-child,.md-typeset details>.admonition-title:last-child,.md-typeset details>summary:last-child{margin-bottom:0}.md-typeset .admonition>.admonition-title:before,.md-typeset .admonition>summary:before,.md-typeset details>.admonition-title:before,.md-typeset details>summary:before{position:absolute;left:1.2rem;color:#448aff;font-size:2rem;content:"\E3C9"}[dir=rtl] .md-typeset .admonition>.admonition-title:before,[dir=rtl] .md-typeset .admonition>summary:before,[dir=rtl] .md-typeset details>.admonition-title:before,[dir=rtl] .md-typeset details>summary:before{right:1.2rem;left:auto}.md-typeset .admonition.abstract,.md-typeset .admonition.summary,.md-typeset .admonition.tldr,.md-typeset details.abstract,.md-typeset details.summary,.md-typeset details.tldr{border-left-color:#00b0ff}[dir=rtl] .md-typeset .admonition.abstract,[dir=rtl] .md-typeset .admonition.summary,[dir=rtl] .md-typeset .admonition.tldr,[dir=rtl] .md-typeset details.abstract,[dir=rtl] .md-typeset details.summary,[dir=rtl] .md-typeset details.tldr{border-right-color:#00b0ff}.md-typeset .admonition.abstract>.admonition-title,.md-typeset .admonition.abstract>summary,.md-typeset .admonition.summary>.admonition-title,.md-typeset .admonition.summary>summary,.md-typeset .admonition.tldr>.admonition-title,.md-typeset .admonition.tldr>summary,.md-typeset details.abstract>.admonition-title,.md-typeset details.abstract>summary,.md-typeset details.summary>.admonition-title,.md-typeset details.summary>summary,.md-typeset details.tldr>.admonition-title,.md-typeset details.tldr>summary{border-bottom-color:.1rem solid rgba(0,176,255,.1);background-color:rgba(0,176,255,.1)}.md-typeset .admonition.abstract>.admonition-title:before,.md-typeset .admonition.abstract>summary:before,.md-typeset .admonition.summary>.admonition-title:before,.md-typeset .admonition.summary>summary:before,.md-typeset .admonition.tldr>.admonition-title:before,.md-typeset .admonition.tldr>summary:before,.md-typeset details.abstract>.admonition-title:before,.md-typeset details.abstract>summary:before,.md-typeset details.summary>.admonition-title:before,.md-typeset details.summary>summary:before,.md-typeset details.tldr>.admonition-title:before,.md-typeset details.tldr>summary:before{color:#00b0ff;content:"\E8D2"}.md-typeset .admonition.info,.md-typeset .admonition.todo,.md-typeset details.info,.md-typeset details.todo{border-left-color:#00b8d4}[dir=rtl] .md-typeset .admonition.info,[dir=rtl] .md-typeset .admonition.todo,[dir=rtl] .md-typeset details.info,[dir=rtl] .md-typeset details.todo{border-right-color:#00b8d4}.md-typeset .admonition.info>.admonition-title,.md-typeset .admonition.info>summary,.md-typeset .admonition.todo>.admonition-title,.md-typeset .admonition.todo>summary,.md-typeset details.info>.admonition-title,.md-typeset details.info>summary,.md-typeset details.todo>.admonition-title,.md-typeset details.todo>summary{border-bottom-color:.1rem solid rgba(0,184,212,.1);background-color:rgba(0,184,212,.1)}.md-typeset .admonition.info>.admonition-title:before,.md-typeset .admonition.info>summary:before,.md-typeset .admonition.todo>.admonition-title:before,.md-typeset .admonition.todo>summary:before,.md-typeset details.info>.admonition-title:before,.md-typeset details.info>summary:before,.md-typeset details.todo>.admonition-title:before,.md-typeset details.todo>summary:before{color:#00b8d4;content:"\E88E"}.md-typeset .admonition.hint,.md-typeset .admonition.important,.md-typeset .admonition.tip,.md-typeset details.hint,.md-typeset details.important,.md-typeset details.tip{border-left-color:#00bfa5}[dir=rtl] .md-typeset .admonition.hint,[dir=rtl] .md-typeset .admonition.important,[dir=rtl] .md-typeset .admonition.tip,[dir=rtl] .md-typeset details.hint,[dir=rtl] .md-typeset details.important,[dir=rtl] .md-typeset details.tip{border-right-color:#00bfa5}.md-typeset .admonition.hint>.admonition-title,.md-typeset .admonition.hint>summary,.md-typeset .admonition.important>.admonition-title,.md-typeset .admonition.important>summary,.md-typeset .admonition.tip>.admonition-title,.md-typeset .admonition.tip>summary,.md-typeset details.hint>.admonition-title,.md-typeset details.hint>summary,.md-typeset details.important>.admonition-title,.md-typeset details.important>summary,.md-typeset details.tip>.admonition-title,.md-typeset details.tip>summary{border-bottom-color:.1rem solid rgba(0,191,165,.1);background-color:rgba(0,191,165,.1)}.md-typeset .admonition.hint>.admonition-title:before,.md-typeset .admonition.hint>summary:before,.md-typeset .admonition.important>.admonition-title:before,.md-typeset .admonition.important>summary:before,.md-typeset .admonition.tip>.admonition-title:before,.md-typeset .admonition.tip>summary:before,.md-typeset details.hint>.admonition-title:before,.md-typeset details.hint>summary:before,.md-typeset details.important>.admonition-title:before,.md-typeset details.important>summary:before,.md-typeset details.tip>.admonition-title:before,.md-typeset details.tip>summary:before{color:#00bfa5;content:"\E80E"}.md-typeset .admonition.check,.md-typeset .admonition.done,.md-typeset .admonition.success,.md-typeset details.check,.md-typeset details.done,.md-typeset details.success{border-left-color:#00c853}[dir=rtl] .md-typeset .admonition.check,[dir=rtl] .md-typeset .admonition.done,[dir=rtl] .md-typeset .admonition.success,[dir=rtl] .md-typeset details.check,[dir=rtl] .md-typeset details.done,[dir=rtl] .md-typeset details.success{border-right-color:#00c853}.md-typeset .admonition.check>.admonition-title,.md-typeset .admonition.check>summary,.md-typeset .admonition.done>.admonition-title,.md-typeset .admonition.done>summary,.md-typeset .admonition.success>.admonition-title,.md-typeset .admonition.success>summary,.md-typeset details.check>.admonition-title,.md-typeset details.check>summary,.md-typeset details.done>.admonition-title,.md-typeset details.done>summary,.md-typeset details.success>.admonition-title,.md-typeset details.success>summary{border-bottom-color:.1rem solid rgba(0,200,83,.1);background-color:rgba(0,200,83,.1)}.md-typeset .admonition.check>.admonition-title:before,.md-typeset .admonition.check>summary:before,.md-typeset .admonition.done>.admonition-title:before,.md-typeset .admonition.done>summary:before,.md-typeset .admonition.success>.admonition-title:before,.md-typeset .admonition.success>summary:before,.md-typeset details.check>.admonition-title:before,.md-typeset details.check>summary:before,.md-typeset details.done>.admonition-title:before,.md-typeset details.done>summary:before,.md-typeset details.success>.admonition-title:before,.md-typeset details.success>summary:before{color:#00c853;content:"\E876"}.md-typeset .admonition.faq,.md-typeset .admonition.help,.md-typeset .admonition.question,.md-typeset details.faq,.md-typeset details.help,.md-typeset details.question{border-left-color:#64dd17}[dir=rtl] .md-typeset .admonition.faq,[dir=rtl] .md-typeset .admonition.help,[dir=rtl] .md-typeset .admonition.question,[dir=rtl] .md-typeset details.faq,[dir=rtl] .md-typeset details.help,[dir=rtl] .md-typeset details.question{border-right-color:#64dd17}.md-typeset .admonition.faq>.admonition-title,.md-typeset .admonition.faq>summary,.md-typeset .admonition.help>.admonition-title,.md-typeset .admonition.help>summary,.md-typeset .admonition.question>.admonition-title,.md-typeset .admonition.question>summary,.md-typeset details.faq>.admonition-title,.md-typeset details.faq>summary,.md-typeset details.help>.admonition-title,.md-typeset details.help>summary,.md-typeset details.question>.admonition-title,.md-typeset details.question>summary{border-bottom-color:.1rem solid rgba(100,221,23,.1);background-color:rgba(100,221,23,.1)}.md-typeset .admonition.faq>.admonition-title:before,.md-typeset .admonition.faq>summary:before,.md-typeset .admonition.help>.admonition-title:before,.md-typeset .admonition.help>summary:before,.md-typeset .admonition.question>.admonition-title:before,.md-typeset .admonition.question>summary:before,.md-typeset details.faq>.admonition-title:before,.md-typeset details.faq>summary:before,.md-typeset details.help>.admonition-title:before,.md-typeset details.help>summary:before,.md-typeset details.question>.admonition-title:before,.md-typeset details.question>summary:before{color:#64dd17;content:"\E887"}.md-typeset .admonition.attention,.md-typeset .admonition.caution,.md-typeset .admonition.warning,.md-typeset details.attention,.md-typeset details.caution,.md-typeset details.warning{border-left-color:#ff9100}[dir=rtl] .md-typeset .admonition.attention,[dir=rtl] .md-typeset .admonition.caution,[dir=rtl] .md-typeset .admonition.warning,[dir=rtl] .md-typeset details.attention,[dir=rtl] .md-typeset details.caution,[dir=rtl] .md-typeset details.warning{border-right-color:#ff9100}.md-typeset .admonition.attention>.admonition-title,.md-typeset .admonition.attention>summary,.md-typeset .admonition.caution>.admonition-title,.md-typeset .admonition.caution>summary,.md-typeset .admonition.warning>.admonition-title,.md-typeset .admonition.warning>summary,.md-typeset details.attention>.admonition-title,.md-typeset details.attention>summary,.md-typeset details.caution>.admonition-title,.md-typeset details.caution>summary,.md-typeset details.warning>.admonition-title,.md-typeset details.warning>summary{border-bottom-color:.1rem solid rgba(255,145,0,.1);background-color:rgba(255,145,0,.1)}.md-typeset .admonition.attention>.admonition-title:before,.md-typeset .admonition.attention>summary:before,.md-typeset .admonition.caution>.admonition-title:before,.md-typeset .admonition.caution>summary:before,.md-typeset .admonition.warning>.admonition-title:before,.md-typeset .admonition.warning>summary:before,.md-typeset details.attention>.admonition-title:before,.md-typeset details.attention>summary:before,.md-typeset details.caution>.admonition-title:before,.md-typeset details.caution>summary:before,.md-typeset details.warning>.admonition-title:before,.md-typeset details.warning>summary:before{color:#ff9100;content:"\E002"}.md-typeset .admonition.fail,.md-typeset .admonition.failure,.md-typeset .admonition.missing,.md-typeset details.fail,.md-typeset details.failure,.md-typeset details.missing{border-left-color:#ff5252}[dir=rtl] .md-typeset .admonition.fail,[dir=rtl] .md-typeset .admonition.failure,[dir=rtl] .md-typeset .admonition.missing,[dir=rtl] .md-typeset details.fail,[dir=rtl] .md-typeset details.failure,[dir=rtl] .md-typeset details.missing{border-right-color:#ff5252}.md-typeset .admonition.fail>.admonition-title,.md-typeset .admonition.fail>summary,.md-typeset .admonition.failure>.admonition-title,.md-typeset .admonition.failure>summary,.md-typeset .admonition.missing>.admonition-title,.md-typeset .admonition.missing>summary,.md-typeset details.fail>.admonition-title,.md-typeset details.fail>summary,.md-typeset details.failure>.admonition-title,.md-typeset details.failure>summary,.md-typeset details.missing>.admonition-title,.md-typeset details.missing>summary{border-bottom-color:.1rem solid rgba(255,82,82,.1);background-color:rgba(255,82,82,.1)}.md-typeset .admonition.fail>.admonition-title:before,.md-typeset .admonition.fail>summary:before,.md-typeset .admonition.failure>.admonition-title:before,.md-typeset .admonition.failure>summary:before,.md-typeset .admonition.missing>.admonition-title:before,.md-typeset .admonition.missing>summary:before,.md-typeset details.fail>.admonition-title:before,.md-typeset details.fail>summary:before,.md-typeset details.failure>.admonition-title:before,.md-typeset details.failure>summary:before,.md-typeset details.missing>.admonition-title:before,.md-typeset details.missing>summary:before{color:#ff5252;content:"\E14C"}.md-typeset .admonition.danger,.md-typeset .admonition.error,.md-typeset details.danger,.md-typeset details.error{border-left-color:#ff1744}[dir=rtl] .md-typeset .admonition.danger,[dir=rtl] .md-typeset .admonition.error,[dir=rtl] .md-typeset details.danger,[dir=rtl] .md-typeset details.error{border-right-color:#ff1744}.md-typeset .admonition.danger>.admonition-title,.md-typeset .admonition.danger>summary,.md-typeset .admonition.error>.admonition-title,.md-typeset .admonition.error>summary,.md-typeset details.danger>.admonition-title,.md-typeset details.danger>summary,.md-typeset details.error>.admonition-title,.md-typeset details.error>summary{border-bottom-color:.1rem solid rgba(255,23,68,.1);background-color:rgba(255,23,68,.1)}.md-typeset .admonition.danger>.admonition-title:before,.md-typeset .admonition.danger>summary:before,.md-typeset .admonition.error>.admonition-title:before,.md-typeset .admonition.error>summary:before,.md-typeset details.danger>.admonition-title:before,.md-typeset details.danger>summary:before,.md-typeset details.error>.admonition-title:before,.md-typeset details.error>summary:before{color:#ff1744;content:"\E3E7"}.md-typeset .admonition.bug,.md-typeset details.bug{border-left-color:#f50057}[dir=rtl] .md-typeset .admonition.bug,[dir=rtl] .md-typeset details.bug{border-right-color:#f50057}.md-typeset .admonition.bug>.admonition-title,.md-typeset .admonition.bug>summary,.md-typeset details.bug>.admonition-title,.md-typeset details.bug>summary{border-bottom-color:.1rem solid rgba(245,0,87,.1);background-color:rgba(245,0,87,.1)}.md-typeset .admonition.bug>.admonition-title:before,.md-typeset .admonition.bug>summary:before,.md-typeset details.bug>.admonition-title:before,.md-typeset details.bug>summary:before{color:#f50057;content:"\E868"}.md-typeset .admonition.example,.md-typeset details.example{border-left-color:#651fff}[dir=rtl] .md-typeset .admonition.example,[dir=rtl] .md-typeset details.example{border-right-color:#651fff}.md-typeset .admonition.example>.admonition-title,.md-typeset .admonition.example>summary,.md-typeset details.example>.admonition-title,.md-typeset details.example>summary{border-bottom-color:.1rem solid rgba(101,31,255,.1);background-color:rgba(101,31,255,.1)}.md-typeset .admonition.example>.admonition-title:before,.md-typeset .admonition.example>summary:before,.md-typeset details.example>.admonition-title:before,.md-typeset details.example>summary:before{color:#651fff;content:"\E242"}.md-typeset .admonition.cite,.md-typeset .admonition.quote,.md-typeset details.cite,.md-typeset details.quote{border-left-color:#9e9e9e}[dir=rtl] .md-typeset .admonition.cite,[dir=rtl] .md-typeset .admonition.quote,[dir=rtl] .md-typeset details.cite,[dir=rtl] .md-typeset details.quote{border-right-color:#9e9e9e}.md-typeset .admonition.cite>.admonition-title,.md-typeset .admonition.cite>summary,.md-typeset .admonition.quote>.admonition-title,.md-typeset .admonition.quote>summary,.md-typeset details.cite>.admonition-title,.md-typeset details.cite>summary,.md-typeset details.quote>.admonition-title,.md-typeset details.quote>summary{border-bottom-color:.1rem solid hsla(0,0%,62%,.1);background-color:hsla(0,0%,62%,.1)}.md-typeset .admonition.cite>.admonition-title:before,.md-typeset .admonition.cite>summary:before,.md-typeset .admonition.quote>.admonition-title:before,.md-typeset .admonition.quote>summary:before,.md-typeset details.cite>.admonition-title:before,.md-typeset details.cite>summary:before,.md-typeset details.quote>.admonition-title:before,.md-typeset details.quote>summary:before{color:#9e9e9e;content:"\E244"}.codehilite .o,.codehilite .ow,.md-typeset .highlight .o,.md-typeset .highlight .ow{color:inherit}.codehilite .ge,.md-typeset .highlight .ge{color:#000}.codehilite .gr,.md-typeset .highlight .gr{color:#a00}.codehilite .gh,.md-typeset .highlight .gh{color:#999}.codehilite .go,.md-typeset .highlight .go{color:#888}.codehilite .gp,.md-typeset .highlight .gp{color:#555}.codehilite .gs,.md-typeset .highlight .gs{color:inherit}.codehilite .gu,.md-typeset .highlight .gu{color:#aaa}.codehilite .gt,.md-typeset .highlight .gt{color:#a00}.codehilite .gd,.md-typeset .highlight .gd{background-color:#fdd}.codehilite .gi,.md-typeset .highlight .gi{background-color:#dfd}.codehilite .k,.md-typeset .highlight .k{color:#3b78e7}.codehilite .kc,.md-typeset .highlight .kc{color:#a71d5d}.codehilite .kd,.codehilite .kn,.md-typeset .highlight .kd,.md-typeset .highlight .kn{color:#3b78e7}.codehilite .kp,.md-typeset .highlight .kp{color:#a71d5d}.codehilite .kr,.codehilite .kt,.md-typeset .highlight .kr,.md-typeset .highlight .kt{color:#3e61a2}.codehilite .c,.codehilite .cm,.md-typeset .highlight .c,.md-typeset .highlight .cm{color:#999}.codehilite .cp,.md-typeset .highlight .cp{color:#666}.codehilite .c1,.codehilite .ch,.codehilite .cs,.md-typeset .highlight .c1,.md-typeset .highlight .ch,.md-typeset .highlight .cs{color:#999}.codehilite .na,.codehilite .nb,.md-typeset .highlight .na,.md-typeset .highlight .nb{color:#c2185b}.codehilite .bp,.md-typeset .highlight .bp{color:#3e61a2}.codehilite .nc,.md-typeset .highlight .nc{color:#c2185b}.codehilite .no,.md-typeset .highlight .no{color:#3e61a2}.codehilite .nd,.codehilite .ni,.md-typeset .highlight .nd,.md-typeset .highlight .ni{color:#666}.codehilite .ne,.codehilite .nf,.md-typeset .highlight .ne,.md-typeset .highlight .nf{color:#c2185b}.codehilite .nl,.md-typeset .highlight .nl{color:#3b5179}.codehilite .nn,.md-typeset .highlight .nn{color:#ec407a}.codehilite .nt,.md-typeset .highlight .nt{color:#3b78e7}.codehilite .nv,.codehilite .vc,.codehilite .vg,.codehilite .vi,.md-typeset .highlight .nv,.md-typeset .highlight .vc,.md-typeset .highlight .vg,.md-typeset .highlight .vi{color:#3e61a2}.codehilite .nx,.md-typeset .highlight .nx{color:#ec407a}.codehilite .il,.codehilite .m,.codehilite .mf,.codehilite .mh,.codehilite .mi,.codehilite .mo,.md-typeset .highlight .il,.md-typeset .highlight .m,.md-typeset .highlight .mf,.md-typeset .highlight .mh,.md-typeset .highlight .mi,.md-typeset .highlight .mo{color:#e74c3c}.codehilite .s,.codehilite .sb,.codehilite .sc,.md-typeset .highlight .s,.md-typeset .highlight .sb,.md-typeset .highlight .sc{color:#0d904f}.codehilite .sd,.md-typeset .highlight .sd{color:#999}.codehilite .s2,.md-typeset .highlight .s2{color:#0d904f}.codehilite .se,.codehilite .sh,.codehilite .si,.codehilite .sx,.md-typeset .highlight .se,.md-typeset .highlight .sh,.md-typeset .highlight .si,.md-typeset .highlight .sx{color:#183691}.codehilite .sr,.md-typeset .highlight .sr{color:#009926}.codehilite .s1,.codehilite .ss,.md-typeset .highlight .s1,.md-typeset .highlight .ss{color:#0d904f}.codehilite .err,.md-typeset .highlight .err{color:#a61717}.codehilite .w,.md-typeset .highlight .w{color:transparent}.codehilite .hll,.md-typeset .highlight .hll{display:block;margin:0 -1.2rem;padding:0 1.2rem;background-color:rgba(255,235,59,.5)}.md-typeset .codehilite,.md-typeset .highlight{position:relative;margin:1em 0;padding:0;border-radius:.2rem;background-color:hsla(0,0%,93%,.5);color:#37474f;line-height:1.4;-webkit-overflow-scrolling:touch}.md-typeset .codehilite code,.md-typeset .codehilite pre,.md-typeset .highlight code,.md-typeset .highlight pre{display:block;margin:0;padding:1.05rem 1.2rem;background-color:transparent;overflow:auto;vertical-align:top}.md-typeset .codehilite code::-webkit-scrollbar,.md-typeset .codehilite pre::-webkit-scrollbar,.md-typeset .highlight code::-webkit-scrollbar,.md-typeset .highlight pre::-webkit-scrollbar{width:.4rem;height:.4rem}.md-typeset .codehilite code::-webkit-scrollbar-thumb,.md-typeset .codehilite pre::-webkit-scrollbar-thumb,.md-typeset .highlight code::-webkit-scrollbar-thumb,.md-typeset .highlight pre::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-typeset .codehilite code::-webkit-scrollbar-thumb:hover,.md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,.md-typeset .highlight code::-webkit-scrollbar-thumb:hover,.md-typeset .highlight pre::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-typeset pre.codehilite,.md-typeset pre.highlight{overflow:visible}.md-typeset pre.codehilite code,.md-typeset pre.highlight code{display:block;padding:1.05rem 1.2rem;overflow:auto}.md-typeset .codehilitetable{display:block;margin:1em 0;border-radius:.2em;font-size:1.6rem;overflow:hidden}.md-typeset .codehilitetable tbody,.md-typeset .codehilitetable td{display:block;padding:0}.md-typeset .codehilitetable tr{display:-webkit-box;display:-ms-flexbox;display:flex}.md-typeset .codehilitetable .codehilite,.md-typeset .codehilitetable .highlight,.md-typeset .codehilitetable .linenodiv{margin:0;border-radius:0}.md-typeset .codehilitetable .linenodiv{padding:1.05rem 1.2rem}.md-typeset .codehilitetable .linenos{background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.26);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.md-typeset .codehilitetable .linenos pre{margin:0;padding:0;background-color:transparent;color:inherit;text-align:right}.md-typeset .codehilitetable .code{-webkit-box-flex:1;-ms-flex:1;flex:1;overflow:hidden}.md-typeset>.codehilitetable{-webkit-box-shadow:none;box-shadow:none}.md-typeset [id^="fnref:"]{display:inline-block}.md-typeset [id^="fnref:"]:target{margin-top:-7.6rem;padding-top:7.6rem;pointer-events:none}.md-typeset [id^="fn:"]:before{display:none;height:0;content:""}.md-typeset [id^="fn:"]:target:before{display:block;margin-top:-7rem;padding-top:7rem;pointer-events:none}.md-typeset .footnote{color:rgba(0,0,0,.54);font-size:1.28rem}.md-typeset .footnote ol{margin-left:0}.md-typeset .footnote li{-webkit-transition:color .25s;transition:color .25s}.md-typeset .footnote li:target{color:rgba(0,0,0,.87)}.md-typeset .footnote li :first-child{margin-top:0}.md-typeset .footnote li:hover .footnote-backref,.md-typeset .footnote li:target .footnote-backref{-webkit-transform:translateX(0);transform:translateX(0);opacity:1}.md-typeset .footnote li:hover .footnote-backref:hover,.md-typeset .footnote li:target .footnote-backref{color:#536dfe}.md-typeset .footnote-ref{display:inline-block;pointer-events:auto}.md-typeset .footnote-ref:before{display:inline;margin:0 .2em;border-left:.1rem solid rgba(0,0,0,.26);font-size:1.25em;content:"";vertical-align:-.5rem}.md-typeset .footnote-backref{display:inline-block;-webkit-transform:translateX(.5rem);transform:translateX(.5rem);-webkit-transition:color .25s,opacity .125s .125s,-webkit-transform .25s .125s;transition:color .25s,opacity .125s .125s,-webkit-transform .25s .125s;transition:transform .25s .125s,color .25s,opacity .125s .125s;transition:transform .25s .125s,color .25s,opacity .125s .125s,-webkit-transform .25s .125s;color:rgba(0,0,0,.26);font-size:0;opacity:0;vertical-align:text-bottom}[dir=rtl] .md-typeset .footnote-backref{-webkit-transform:translateX(-.5rem);transform:translateX(-.5rem)}.md-typeset .footnote-backref:before{display:inline-block;font-size:1.6rem;content:"\E31B"}[dir=rtl] .md-typeset .footnote-backref:before{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.md-typeset .headerlink{display:inline-block;margin-left:1rem;-webkit-transform:translateY(.5rem);transform:translateY(.5rem);-webkit-transition:color .25s,opacity .125s .25s,-webkit-transform .25s .25s;transition:color .25s,opacity .125s .25s,-webkit-transform .25s .25s;transition:transform .25s .25s,color .25s,opacity .125s .25s;transition:transform .25s .25s,color .25s,opacity .125s .25s,-webkit-transform .25s .25s;opacity:0}[dir=rtl] .md-typeset .headerlink{margin-right:1rem;margin-left:0}html body .md-typeset .headerlink{color:rgba(0,0,0,.26)}.md-typeset h1[id] .headerlink{display:none}.md-typeset h2[id]:before{display:block;margin-top:-.8rem;padding-top:.8rem;content:""}.md-typeset h2[id]:target:before{margin-top:-6.8rem;padding-top:6.8rem}.md-typeset h2[id] .headerlink:focus,.md-typeset h2[id]:hover .headerlink,.md-typeset h2[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h2[id] .headerlink:focus,.md-typeset h2[id]:hover .headerlink:hover,.md-typeset h2[id]:target .headerlink{color:#536dfe}.md-typeset h3[id]:before{display:block;margin-top:-.9rem;padding-top:.9rem;content:""}.md-typeset h3[id]:target:before{margin-top:-6.9rem;padding-top:6.9rem}.md-typeset h3[id] .headerlink:focus,.md-typeset h3[id]:hover .headerlink,.md-typeset h3[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h3[id] .headerlink:focus,.md-typeset h3[id]:hover .headerlink:hover,.md-typeset h3[id]:target .headerlink{color:#536dfe}.md-typeset h4[id]:before{display:block;margin-top:-.9rem;padding-top:.9rem;content:""}.md-typeset h4[id]:target:before{margin-top:-6.9rem;padding-top:6.9rem}.md-typeset h4[id] .headerlink:focus,.md-typeset h4[id]:hover .headerlink,.md-typeset h4[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h4[id] .headerlink:focus,.md-typeset h4[id]:hover .headerlink:hover,.md-typeset h4[id]:target .headerlink{color:#536dfe}.md-typeset h5[id]:before{display:block;margin-top:-1.1rem;padding-top:1.1rem;content:""}.md-typeset h5[id]:target:before{margin-top:-7.1rem;padding-top:7.1rem}.md-typeset h5[id] .headerlink:focus,.md-typeset h5[id]:hover .headerlink,.md-typeset h5[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h5[id] .headerlink:focus,.md-typeset h5[id]:hover .headerlink:hover,.md-typeset h5[id]:target .headerlink{color:#536dfe}.md-typeset h6[id]:before{display:block;margin-top:-1.1rem;padding-top:1.1rem;content:""}.md-typeset h6[id]:target:before{margin-top:-7.1rem;padding-top:7.1rem}.md-typeset h6[id] .headerlink:focus,.md-typeset h6[id]:hover .headerlink,.md-typeset h6[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h6[id] .headerlink:focus,.md-typeset h6[id]:hover .headerlink:hover,.md-typeset h6[id]:target .headerlink{color:#536dfe}.md-typeset .MJXc-display{margin:.75em 0;padding:.75em 0;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset .MathJax_CHTML{outline:0}.md-typeset .critic.comment,.md-typeset del.critic,.md-typeset ins.critic{margin:0 .25em;padding:.0625em 0;border-radius:.2rem;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset del.critic{background-color:#fdd;-webkit-box-shadow:.25em 0 0 #fdd,-.25em 0 0 #fdd;box-shadow:.25em 0 0 #fdd,-.25em 0 0 #fdd}.md-typeset ins.critic{background-color:#dfd;-webkit-box-shadow:.25em 0 0 #dfd,-.25em 0 0 #dfd;box-shadow:.25em 0 0 #dfd,-.25em 0 0 #dfd}.md-typeset .critic.comment{background-color:hsla(0,0%,93%,.5);color:#37474f;-webkit-box-shadow:.25em 0 0 hsla(0,0%,93%,.5),-.25em 0 0 hsla(0,0%,93%,.5);box-shadow:.25em 0 0 hsla(0,0%,93%,.5),-.25em 0 0 hsla(0,0%,93%,.5)}.md-typeset .critic.comment:before{padding-right:.125em;color:rgba(0,0,0,.26);content:"\E0B7";vertical-align:-.125em}.md-typeset .critic.block{display:block;margin:1em 0;padding-right:1.6rem;padding-left:1.6rem;-webkit-box-shadow:none;box-shadow:none}.md-typeset .critic.block :first-child{margin-top:.5em}.md-typeset .critic.block :last-child{margin-bottom:.5em}.md-typeset details{padding-top:0}.md-typeset details[open]>summary:after{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.md-typeset details:not([open]){padding-bottom:0}.md-typeset details:not([open])>summary{border-bottom:none}.md-typeset details summary{padding-right:4rem}[dir=rtl] .md-typeset details summary{padding-left:4rem}.no-details .md-typeset details:not([open])>*{display:none}.no-details .md-typeset details:not([open]) summary{display:block}.md-typeset summary{display:block;outline:none;cursor:pointer}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset summary:after{position:absolute;top:.8rem;right:1.2rem;color:rgba(0,0,0,.26);font-size:2rem;content:"\E313"}[dir=rtl] .md-typeset summary:after{right:auto;left:1.2rem}.md-typeset .emojione{width:2rem;vertical-align:text-top}.md-typeset code.codehilite,.md-typeset code.highlight{margin:0 .29412em;padding:.07353em 0}.md-typeset .task-list-item{position:relative;list-style-type:none}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em;left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em;left:auto}.md-typeset .task-list-control .task-list-indicator:before{position:absolute;top:.15em;left:-1.25em;color:rgba(0,0,0,.26);font-size:1.25em;content:"\E835";vertical-align:-.25em}[dir=rtl] .md-typeset .task-list-control .task-list-indicator:before{right:-1.25em;left:auto}.md-typeset .task-list-control [type=checkbox]:checked+.task-list-indicator:before{content:"\E834"}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}@media print{.md-typeset a:after{color:rgba(0,0,0,.54);content:" [" attr(href) "]"}.md-typeset code,.md-typeset pre{white-space:pre-wrap}.md-typeset code{-webkit-box-shadow:none;box-shadow:none;-webkit-box-decoration-break:initial;box-decoration-break:slice}.md-clipboard,.md-content__icon,.md-footer,.md-header,.md-sidebar,.md-tabs,.md-typeset .headerlink{display:none}}@media only screen and (max-width:44.9375em){.md-typeset pre{margin:1em -1.6rem;border-radius:0}.md-typeset pre>code{padding:1.05rem 1.6rem}.md-footer-nav__link--prev .md-footer-nav__title{display:none}.md-search-result__teaser{max-height:5rem;-webkit-line-clamp:3}.codehilite .hll,.md-typeset .highlight .hll{margin:0 -1.6rem;padding:0 1.6rem}.md-typeset>.codehilite,.md-typeset>.highlight{margin:1em -1.6rem;border-radius:0}.md-typeset>.codehilite code,.md-typeset>.codehilite pre,.md-typeset>.highlight code,.md-typeset>.highlight pre{padding:1.05rem 1.6rem}.md-typeset>.codehilitetable{margin:1em -1.6rem;border-radius:0}.md-typeset>.codehilitetable .codehilite>code,.md-typeset>.codehilitetable .codehilite>pre,.md-typeset>.codehilitetable .highlight>code,.md-typeset>.codehilitetable .highlight>pre,.md-typeset>.codehilitetable .linenodiv{padding:1rem 1.6rem}.md-typeset>p>.MJXc-display{margin:.75em -1.6rem;padding:.25em 1.6rem}}@media only screen and (min-width:100em){html{font-size:68.75%}}@media only screen and (min-width:125em){html{font-size:75%}}@media only screen and (max-width:59.9375em){body[data-md-state=lock]{overflow:hidden}.ios body[data-md-state=lock] .md-container{display:none}html .md-nav__link[for=toc]{display:block;padding-right:4.8rem}html .md-nav__link[for=toc]:after{color:inherit;content:"\E8DE"}html .md-nav__link[for=toc]+.md-nav__link{display:none}html .md-nav__link[for=toc]~.md-nav{display:-webkit-box;display:-ms-flexbox;display:flex}html [dir=rtl] .md-nav__link{padding-right:1.6rem;padding-left:4.8rem}.md-nav__source{display:block;padding:0 .4rem;background-color:rgba(50,64,144,.9675);color:#fff}.md-search__overlay{position:absolute;top:.4rem;left:.4rem;width:3.6rem;height:3.6rem;-webkit-transform-origin:center;transform-origin:center;-webkit-transition:opacity .2s .2s,-webkit-transform .3s .1s;transition:opacity .2s .2s,-webkit-transform .3s .1s;transition:transform .3s .1s,opacity .2s .2s;transition:transform .3s .1s,opacity .2s .2s,-webkit-transform .3s .1s;border-radius:2rem;background-color:#fff;overflow:hidden;pointer-events:none}[dir=rtl] .md-search__overlay{right:.4rem;left:auto}[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transition:opacity .1s,-webkit-transform .4s;transition:opacity .1s,-webkit-transform .4s;transition:transform .4s,opacity .1s;transition:transform .4s,opacity .1s,-webkit-transform .4s;opacity:1}.md-search__inner{position:fixed;top:0;left:100%;width:100%;height:100%;-webkit-transform:translateX(5%);transform:translateX(5%);-webkit-transition:right 0s .3s,left 0s .3s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;transition:right 0s .3s,left 0s .3s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;transition:right 0s .3s,left 0s .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;transition:right 0s .3s,left 0s .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;opacity:0;z-index:2}[data-md-toggle=search]:checked~.md-header .md-search__inner{left:0;-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:right 0s 0s,left 0s 0s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;transition:right 0s 0s,left 0s 0s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;transition:right 0s 0s,left 0s 0s,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;transition:right 0s 0s,left 0s 0s,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;opacity:1}[dir=rtl] [data-md-toggle=search]:checked~.md-header .md-search__inner{right:0;left:auto}html [dir=rtl] .md-search__inner{right:100%;left:auto;-webkit-transform:translateX(-5%);transform:translateX(-5%)}.md-search__input{width:100%;height:4.8rem;font-size:1.8rem}.md-search__icon[for=search]{top:1.2rem;left:1.6rem}.md-search__icon[for=search][for=search]:before{content:"\E5C4"}[dir=rtl] .md-search__icon[for=search][for=search]:before{content:"\E5C8"}.md-search__icon[type=reset]{top:1.2rem;right:1.6rem}.md-search__output{top:4.8rem;bottom:0}.md-search-result__article--document:before{display:none}}@media only screen and (max-width:76.1875em){[data-md-toggle=drawer]:checked~.md-overlay{width:100%;height:100%;-webkit-transition:width 0s,height 0s,opacity .25s;transition:width 0s,height 0s,opacity .25s;opacity:1}.md-header-nav__button.md-icon--home,.md-header-nav__button.md-logo{display:none}.md-hero__inner{margin-top:4.8rem;margin-bottom:2.4rem}.md-nav{background-color:#fff}.md-nav--primary,.md-nav--primary .md-nav{display:-webkit-box;display:-ms-flexbox;display:flex;position:absolute;top:0;right:0;left:0;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:100%;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:1.6rem;line-height:1.5}html .md-nav--primary .md-nav__title{position:relative;height:11.2rem;padding:6rem 1.6rem .4rem;background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.54);font-weight:400;line-height:4.8rem;white-space:nowrap;cursor:pointer}html .md-nav--primary .md-nav__title:before{display:block;position:absolute;top:.4rem;left:.4rem;width:4rem;height:4rem;color:rgba(0,0,0,.54)}html .md-nav--primary .md-nav__title~.md-nav__list{background-color:#fff;-webkit-box-shadow:0 .1rem 0 rgba(0,0,0,.07) inset;box-shadow:inset 0 .1rem 0 rgba(0,0,0,.07)}html .md-nav--primary .md-nav__title~.md-nav__list>.md-nav__item:first-child{border-top:0}html .md-nav--primary .md-nav__title--site{position:relative;background-color:#3f51b5;color:#fff}html .md-nav--primary .md-nav__title--site .md-nav__button{display:block;position:absolute;top:.4rem;left:.4rem;width:6.4rem;height:6.4rem;font-size:4.8rem}html .md-nav--primary .md-nav__title--site:before{display:none}html [dir=rtl] .md-nav--primary .md-nav__title--site .md-nav__button,html [dir=rtl] .md-nav--primary .md-nav__title:before{right:.4rem;left:auto}.md-nav--primary .md-nav__list{-webkit-box-flex:1;-ms-flex:1;flex:1;overflow-y:auto}.md-nav--primary .md-nav__item{padding:0;border-top:.1rem solid rgba(0,0,0,.07)}[dir=rtl] .md-nav--primary .md-nav__item{padding:0}.md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:4.8rem}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:1.6rem;padding-left:4.8rem}.md-nav--primary .md-nav__item--nested>.md-nav__link:after{content:"\E315"}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link:after{content:"\E314"}.md-nav--primary .md-nav__link{position:relative;margin-top:0;padding:1.2rem 1.6rem}.md-nav--primary .md-nav__link:after{position:absolute;top:50%;right:1.2rem;margin-top:-1.2rem;color:inherit;font-size:2.4rem}[dir=rtl] .md-nav--primary .md-nav__link:after{right:auto;left:1.2rem}.md-nav--primary .md-nav--secondary .md-nav__link{position:static}.md-nav--primary .md-nav--secondary .md-nav{position:static;background-color:transparent}.md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:2.8rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:2.8rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:4rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:5.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:5.2rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:6.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:6.4rem;padding-left:0}.md-nav__toggle~.md-nav{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-transform:translateX(100%);transform:translateX(100%);-webkit-transition:opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);transition:opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity .125s .05s;transition:transform .25s cubic-bezier(.8,0,.6,1),opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);opacity:0}[dir=rtl] .md-nav__toggle~.md-nav{-webkit-transform:translateX(-100%);transform:translateX(-100%)}.no-csstransforms3d .md-nav__toggle~.md-nav{display:none}.md-nav__toggle:checked~.md-nav{-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .125s .125s;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1}.no-csstransforms3d .md-nav__toggle:checked~.md-nav{display:-webkit-box;display:-ms-flexbox;display:flex}.md-sidebar--primary{position:fixed;top:0;left:-24.2rem;width:24.2rem;height:100%;-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:-webkit-transform .25s cubic-bezier(.4,0,.2,1),-webkit-box-shadow .25s;transition:-webkit-transform .25s cubic-bezier(.4,0,.2,1),-webkit-box-shadow .25s;transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1),-webkit-box-shadow .25s;background-color:#fff;z-index:3}[dir=rtl] .md-sidebar--primary{right:-24.2rem;left:auto}.no-csstransforms3d .md-sidebar--primary{display:none}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{-webkit-box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);-webkit-transform:translateX(24.2rem);transform:translateX(24.2rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{-webkit-transform:translateX(-24.2rem);transform:translateX(-24.2rem)}.no-csstransforms3d [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{display:block}.md-sidebar--primary .md-sidebar__scrollwrap{overflow:hidden;position:absolute;top:0;right:0;bottom:0;left:0;margin:0}.md-tabs{display:none}}@media only screen and (min-width:60em){.md-content{margin-right:24.2rem}[dir=rtl] .md-content{margin-right:0;margin-left:24.2rem}.md-header-nav__button.md-icon--search{display:none}.md-header-nav__source{display:block;width:23rem;max-width:23rem;margin-left:2.8rem;padding-right:1.2rem}[dir=rtl] .md-header-nav__source{margin-right:2.8rem;margin-left:0;padding-right:0;padding-left:1.2rem}.md-search{padding:.4rem}.md-search__overlay{position:fixed;top:0;left:0;width:0;height:0;-webkit-transition:width 0s .25s,height 0s .25s,opacity .25s;transition:width 0s .25s,height 0s .25s,opacity .25s;background-color:rgba(0,0,0,.54);cursor:pointer}[dir=rtl] .md-search__overlay{right:0;left:auto}[data-md-toggle=search]:checked~.md-header .md-search__overlay{width:100%;height:100%;-webkit-transition:width 0s,height 0s,opacity .25s;transition:width 0s,height 0s,opacity .25s;opacity:1}.md-search__inner{position:relative;width:23rem;padding:.2rem 0;float:right;-webkit-transition:width .25s cubic-bezier(.1,.7,.1,1);transition:width .25s cubic-bezier(.1,.7,.1,1)}[dir=rtl] .md-search__inner{float:left}.md-search__form,.md-search__input{border-radius:.2rem}.md-search__input{width:100%;height:3.6rem;padding-left:4.4rem;-webkit-transition:background-color .25s cubic-bezier(.1,.7,.1,1),color .25s cubic-bezier(.1,.7,.1,1);transition:background-color .25s cubic-bezier(.1,.7,.1,1),color .25s cubic-bezier(.1,.7,.1,1);background-color:rgba(0,0,0,.26);color:inherit;font-size:1.6rem}[dir=rtl] .md-search__input{padding-right:4.4rem}.md-search__input+.md-search__icon{color:inherit}.md-search__input::-webkit-input-placeholder{color:hsla(0,0%,100%,.7)}.md-search__input:-ms-input-placeholder,.md-search__input::-ms-input-placeholder{color:hsla(0,0%,100%,.7)}.md-search__input::placeholder{color:hsla(0,0%,100%,.7)}.md-search__input:hover{background-color:hsla(0,0%,100%,.12)}[data-md-toggle=search]:checked~.md-header .md-search__input{border-radius:.2rem .2rem 0 0;background-color:#fff;color:rgba(0,0,0,.87);text-overflow:none}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::-webkit-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input:-ms-input-placeholder,[data-md-toggle=search]:checked~.md-header .md-search__input::-ms-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:rgba(0,0,0,.54)}.md-search__output{top:3.8rem;-webkit-transition:opacity .4s;transition:opacity .4s;opacity:0}[data-md-toggle=search]:checked~.md-header .md-search__output{-webkit-box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);opacity:1}.md-search__scrollwrap{max-height:0}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap::-webkit-scrollbar{width:.4rem;height:.4rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-search-result__meta{padding-left:4.4rem}[dir=rtl] .md-search-result__meta{padding-right:4.4rem;padding-left:0}.md-search-result__article{padding-left:4.4rem}[dir=rtl] .md-search-result__article{padding-right:4.4rem;padding-left:1.6rem}.md-sidebar--secondary{display:block;margin-left:100%;-webkit-transform:translate(-100%);transform:translate(-100%)}[dir=rtl] .md-sidebar--secondary{margin-right:100%;margin-left:0;-webkit-transform:translate(100%);transform:translate(100%)}}@media only screen and (min-width:76.25em){.md-content{margin-left:24.2rem}[dir=rtl] .md-content{margin-right:24.2rem}.md-content__inner{margin-right:2.4rem;margin-left:2.4rem}.md-header-nav__button.md-icon--menu{display:none}.md-nav[data-md-state=animate]{-webkit-transition:max-height .25s cubic-bezier(.86,0,.07,1);transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav__toggle~.md-nav{max-height:0;overflow:hidden}.no-js .md-nav__toggle~.md-nav{display:none}.md-nav[data-md-state=expand],.md-nav__toggle:checked~.md-nav{max-height:100%}.no-js .md-nav[data-md-state=expand],.no-js .md-nav__toggle:checked~.md-nav{display:block}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--nested>.md-nav__link:after{display:inline-block;-webkit-transform-origin:.45em .45em;transform-origin:.45em .45em;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;vertical-align:-.125em}.js .md-nav__item--nested>.md-nav__link:after{-webkit-transition:-webkit-transform .4s;transition:-webkit-transform .4s;transition:transform .4s;transition:transform .4s,-webkit-transform .4s}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link:after{-webkit-transform:rotateX(180deg);transform:rotateX(180deg)}.md-search__scrollwrap,[data-md-toggle=search]:checked~.md-header .md-search__inner{width:68.8rem}.md-sidebar--secondary{margin-left:122rem}[dir=rtl] .md-sidebar--secondary{margin-right:122rem;margin-left:0}.md-tabs~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{font-size:0;visibility:hidden}.md-tabs--active~.md-main .md-nav--primary .md-nav__title{display:block;padding:0}.md-tabs--active~.md-main .md-nav--primary .md-nav__title--site{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item{font-size:0;visibility:hidden}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{display:none;font-size:1.4rem;overflow:auto;visibility:visible}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested>.md-nav__link{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--active{display:block}.md-tabs--active~.md-main .md-nav[data-md-level="1"]{max-height:none;overflow:visible}.md-tabs--active~.md-main .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-left:0}.md-tabs--active~.md-main .md-nav[data-md-level="1"] .md-nav .md-nav__title{display:none}}@media only screen and (min-width:45em){.md-footer-nav__link{width:50%}.md-footer-copyright{max-width:75%;float:left}[dir=rtl] .md-footer-copyright{float:right}.md-footer-social{padding:1.2rem 0;float:right}[dir=rtl] .md-footer-social{float:left}}@media only screen and (max-width:29.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(45);transform:scale(45)}}@media only screen and (min-width:30em) and (max-width:44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(60);transform:scale(60)}}@media only screen and (min-width:45em) and (max-width:59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(75);transform:scale(75)}}@media only screen and (min-width:60em) and (max-width:76.1875em){.md-search__scrollwrap,[data-md-toggle=search]:checked~.md-header .md-search__inner{width:46.8rem}.md-search-result__teaser{max-height:5rem;-webkit-line-clamp:3}} +/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJhc3NldHMvc3R5bGVzaGVldHMvYXBwbGljYXRpb24uMmE4ODAwOGEuY3NzIiwic291cmNlUm9vdCI6IiJ9*/ \ No newline at end of file diff --git a/docs/tools/mkdocs-material-theme/base.html b/docs/tools/mkdocs-material-theme/base.html index b9fdb82d7c7..53e855a19ed 100644 --- a/docs/tools/mkdocs-material-theme/base.html +++ b/docs/tools/mkdocs-material-theme/base.html @@ -47,7 +47,7 @@ {% endif %} {% endblock %} {% block styles %} - + {% if palette.primary or palette.accent %} {% endif %} @@ -67,18 +67,13 @@ {% endfor %} {% block extrahead %}{% endblock %} + {% set direction = config.theme.direction %} {% if palette.primary or palette.accent %} {% set primary = palette.primary | replace(" ", "-") | lower %} {% set accent = palette.accent | replace(" ", "-") | lower %} - {% if palette.primary and palette.accent %} - - {% elif palette.primary %} - - {% elif palette.accent %} - - {% endif %} + {% else %} - + {% endif %} diff --git a/docs/tools/mkdocs-material-theme/partials/language/fa.html b/docs/tools/mkdocs-material-theme/partials/language/fa.html new file mode 100644 index 00000000000..54a7d2860d3 --- /dev/null +++ b/docs/tools/mkdocs-material-theme/partials/language/fa.html @@ -0,0 +1,22 @@ +{% macro t(key) %}{{ { + "language": "fa", + "direction": "rtl", + "clipboard.copy": "کپی کردن", + "clipboard.copied": "کپی شد", + "edit.link.title": "این صفحه را ویرایش کنید", + "footer.previous": "قبلی", + "footer.next": "بعدی", + "meta.comments": "نظرات", + "meta.source": "منبع", + "search.language": "", + "search.pipeline.stopwords": false, + "search.pipeline.trimmer": false, + "search.placeholder": "جستجو", + "search.result.placeholder": "برای شروع جستجو تایپ کنید", + "search.result.none": "سندی یافت نشد", + "search.result.one": "1 سند یافت شد", + "search.result.other": "# سند یافت شد", + "skip.link.title": "پرش به محتویات", + "source.link.title": "رفتن به مخزن", + "toc.title": "فهرست موضوعات" +}[key] }}{% endmacro %} From 6fffc9d7312732f0f7bc53705c20e65612092663 Mon Sep 17 00:00:00 2001 From: BayoNet Date: Wed, 5 Sep 2018 17:17:02 +0300 Subject: [PATCH 02/78] Fix for language direction in data_types --- docs/fa/data_types/index.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/fa/data_types/index.md b/docs/fa/data_types/index.md index 0f3e5f17609..acd91711ba6 100644 --- a/docs/fa/data_types/index.md +++ b/docs/fa/data_types/index.md @@ -1,7 +1,11 @@ +
+ # Data types ClickHouse قابلیت ذخیره سازی انواع type های مختلف برای ذخیره داده ها در جداول را دارا می باشد. این بخش انواع data type های قابل پشتیبانی در ClickHouse را شرح می دهد، همچنین ملاحطات آنها در هنگام استفاده آنها را شرح می دهد. + +
From c377389f62dbaee3a142837646e88c1064d9265b Mon Sep 17 00:00:00 2001 From: VadimPE Date: Thu, 20 Sep 2018 17:34:30 +0300 Subject: [PATCH 03/78] CLICKHOUSE-3947 add LIKE and IN () --- dbms/src/Storages/transformQueryForExternalDatabase.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dbms/src/Storages/transformQueryForExternalDatabase.cpp b/dbms/src/Storages/transformQueryForExternalDatabase.cpp index 5e08dc8ce93..0827edfd42e 100644 --- a/dbms/src/Storages/transformQueryForExternalDatabase.cpp +++ b/dbms/src/Storages/transformQueryForExternalDatabase.cpp @@ -9,6 +9,8 @@ #include #include +#include + namespace DB { @@ -24,6 +26,9 @@ static bool isCompatible(const IAST & node) || name == "not" || name == "equals" || name == "notEquals" + || name == "like" + || name == "notLike" + || name == "in" || name == "greater" || name == "less" || name == "lessOrEquals" @@ -112,6 +117,8 @@ String transformQueryForExternalDatabase( settings.identifier_quoting_style = identifier_quoting_style; select->format(settings); + + std::cout << (out.str()); return out.str(); } From e93f56bb50f6646763e6ced258e344b581f7981d Mon Sep 17 00:00:00 2001 From: VadimPE Date: Thu, 20 Sep 2018 17:37:55 +0300 Subject: [PATCH 04/78] fix --- .../queries/0_stateless/00717_default_join_type.sql | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00717_default_join_type.sql diff --git a/dbms/tests/queries/0_stateless/00717_default_join_type.sql b/dbms/tests/queries/0_stateless/00717_default_join_type.sql new file mode 100644 index 00000000000..4249790747c --- /dev/null +++ b/dbms/tests/queries/0_stateless/00717_default_join_type.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS test.default_join1; +DROP TABLE IF EXISTS test.default_join2; + +CREATE TABLE test.default_join1(a Int64, b Int64) ENGINE=Memory; +CREATE TABLE test.default_join2(a Int64, b Int64) ENGINE=Memory; + +INSERT INTO test.default_join1 VALUES(1, 1), (2, 2), (3, 3); +INSERT INTO test.default_join2 VALUES(3, 3), (4, 4); + +SELECT a, b FROM test.default_join1 JOIN (SELECT a, b FROM test.default_join2) USING a ORDER BY b SETTINGS join_default_strictness='ANY'; From 3547334ba541a93c9e31ef927d8a8b8368b46f14 Mon Sep 17 00:00:00 2001 From: VadimPE Date: Thu, 20 Sep 2018 17:39:02 +0300 Subject: [PATCH 05/78] fix --- .../src/Storages/transformQueryForExternalDatabase.cpp | 3 --- .../queries/0_stateless/00717_default_join_type.sql | 10 ---------- 2 files changed, 13 deletions(-) delete mode 100644 dbms/tests/queries/0_stateless/00717_default_join_type.sql diff --git a/dbms/src/Storages/transformQueryForExternalDatabase.cpp b/dbms/src/Storages/transformQueryForExternalDatabase.cpp index 0827edfd42e..3fbf1143b7a 100644 --- a/dbms/src/Storages/transformQueryForExternalDatabase.cpp +++ b/dbms/src/Storages/transformQueryForExternalDatabase.cpp @@ -9,8 +9,6 @@ #include #include -#include - namespace DB { @@ -118,7 +116,6 @@ String transformQueryForExternalDatabase( select->format(settings); - std::cout << (out.str()); return out.str(); } diff --git a/dbms/tests/queries/0_stateless/00717_default_join_type.sql b/dbms/tests/queries/0_stateless/00717_default_join_type.sql deleted file mode 100644 index 4249790747c..00000000000 --- a/dbms/tests/queries/0_stateless/00717_default_join_type.sql +++ /dev/null @@ -1,10 +0,0 @@ -DROP TABLE IF EXISTS test.default_join1; -DROP TABLE IF EXISTS test.default_join2; - -CREATE TABLE test.default_join1(a Int64, b Int64) ENGINE=Memory; -CREATE TABLE test.default_join2(a Int64, b Int64) ENGINE=Memory; - -INSERT INTO test.default_join1 VALUES(1, 1), (2, 2), (3, 3); -INSERT INTO test.default_join2 VALUES(3, 3), (4, 4); - -SELECT a, b FROM test.default_join1 JOIN (SELECT a, b FROM test.default_join2) USING a ORDER BY b SETTINGS join_default_strictness='ANY'; From 60653fc04246dcbf535ca0ddefe1aafaba0e4837 Mon Sep 17 00:00:00 2001 From: VadimPE Date: Fri, 21 Sep 2018 14:33:58 +0300 Subject: [PATCH 06/78] CLICKHOUSE-3947 fix style --- dbms/src/Storages/transformQueryForExternalDatabase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/src/Storages/transformQueryForExternalDatabase.cpp b/dbms/src/Storages/transformQueryForExternalDatabase.cpp index 3fbf1143b7a..64e861a0a0b 100644 --- a/dbms/src/Storages/transformQueryForExternalDatabase.cpp +++ b/dbms/src/Storages/transformQueryForExternalDatabase.cpp @@ -26,7 +26,7 @@ static bool isCompatible(const IAST & node) || name == "notEquals" || name == "like" || name == "notLike" - || name == "in" + || name == "in" || name == "greater" || name == "less" || name == "lessOrEquals" From 679422a0b3bdb1164d31ffb28fe7137857af3d01 Mon Sep 17 00:00:00 2001 From: chertus Date: Fri, 21 Sep 2018 18:20:23 +0300 Subject: [PATCH 07/78] explain ast [CLICKHOUSE-4001] --- dbms/src/Core/iostream_debug_helpers.cpp | 2 + .../Interpreters/InterpreterExplainQuery.cpp | 51 +++++++++++++++++++ .../Interpreters/InterpreterExplainQuery.h | 32 ++++++++++++ dbms/src/Interpreters/InterpreterFactory.cpp | 6 +++ dbms/src/Parsers/ASTExplainQuery.h | 45 ++++++++++++++++ dbms/src/Parsers/IAST.h | 21 ++++++++ dbms/src/Parsers/ParserQueryWithOutput.cpp | 15 +++++- 7 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 dbms/src/Interpreters/InterpreterExplainQuery.cpp create mode 100644 dbms/src/Interpreters/InterpreterExplainQuery.h create mode 100644 dbms/src/Parsers/ASTExplainQuery.h diff --git a/dbms/src/Core/iostream_debug_helpers.cpp b/dbms/src/Core/iostream_debug_helpers.cpp index 4500538b69e..6cbef6ac2d4 100644 --- a/dbms/src/Core/iostream_debug_helpers.cpp +++ b/dbms/src/Core/iostream_debug_helpers.cpp @@ -95,6 +95,7 @@ std::ostream & operator<<(std::ostream & stream, const Connection::Packet & what return stream; } +#if 0 std::ostream & operator<<(std::ostream & stream, const IAST & what) { stream << "IAST{"; @@ -102,5 +103,6 @@ std::ostream & operator<<(std::ostream & stream, const IAST & what) stream << "}"; return stream; } +#endif } diff --git a/dbms/src/Interpreters/InterpreterExplainQuery.cpp b/dbms/src/Interpreters/InterpreterExplainQuery.cpp new file mode 100644 index 00000000000..d7aac9c81a3 --- /dev/null +++ b/dbms/src/Interpreters/InterpreterExplainQuery.cpp @@ -0,0 +1,51 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +BlockIO InterpreterExplainQuery::execute() +{ + BlockIO res; + res.in = executeImpl(); + return res; +} + + +Block InterpreterExplainQuery::getSampleBlock() +{ + Block block; + + ColumnWithTypeAndName col; + col.name = "ast"; + col.type = std::make_shared(); + col.column = col.type->createColumn(); + block.insert(col); + + return block; +} + + +BlockInputStreamPtr InterpreterExplainQuery::executeImpl() +{ + const ASTExplainQuery & ast = typeid_cast(*query); + + std::stringstream ss; + ast.dumpSExpressions(ss); + + Block sample_block = getSampleBlock(); + MutableColumns res_columns = sample_block.cloneEmptyColumns(); + res_columns[0]->insert(ss.str()); + + return std::make_shared(sample_block.cloneWithColumns(std::move(res_columns))); +} + +} diff --git a/dbms/src/Interpreters/InterpreterExplainQuery.h b/dbms/src/Interpreters/InterpreterExplainQuery.h new file mode 100644 index 00000000000..828e10ecd0b --- /dev/null +++ b/dbms/src/Interpreters/InterpreterExplainQuery.h @@ -0,0 +1,32 @@ +#pragma once + +#include + + +namespace DB +{ + +class IAST; +using ASTPtr = std::shared_ptr; + + +/// Returns single row with explain results +class InterpreterExplainQuery : public IInterpreter +{ +public: + InterpreterExplainQuery(const ASTPtr & query_, const Context & ) + : query(query_) + {} + + BlockIO execute() override; + + static Block getSampleBlock(); + +private: + ASTPtr query; + + BlockInputStreamPtr executeImpl(); +}; + + +} diff --git a/dbms/src/Interpreters/InterpreterFactory.cpp b/dbms/src/Interpreters/InterpreterFactory.cpp index 673e80ceabf..5d1b259cc0d 100644 --- a/dbms/src/Interpreters/InterpreterFactory.cpp +++ b/dbms/src/Interpreters/InterpreterFactory.cpp @@ -12,12 +12,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -145,6 +147,10 @@ std::unique_ptr InterpreterFactory::get(ASTPtr & query, Context & { return std::make_unique(query, context); } + else if (typeid_cast(query.get())) + { + return std::make_unique(query, context); + } else if (typeid_cast(query.get())) { return std::make_unique(query, context); diff --git a/dbms/src/Parsers/ASTExplainQuery.h b/dbms/src/Parsers/ASTExplainQuery.h new file mode 100644 index 00000000000..b02731c79d3 --- /dev/null +++ b/dbms/src/Parsers/ASTExplainQuery.h @@ -0,0 +1,45 @@ +#pragma once + +#include + + +namespace DB +{ + + +/// AST, EXPLAIN or other query with meaning of explanation query instead of execution +class ASTExplainQuery : public IAST +{ +public: + enum ExplainKind + { + ParsedAST, + }; + + ASTExplainQuery(ExplainKind kind_ = ParsedAST) + : kind(kind_) + {} + + String getID() const override { return "Explain_" + toString(kind); } + ASTPtr clone() const override { return std::make_shared(*this); } + +protected: + void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << toString(kind) << (settings.hilite ? hilite_none : ""); + } + +private: + ExplainKind kind; + + static String toString(ExplainKind kind) + { + switch (kind) + { + case ParsedAST: return "ParsedAST"; + } + __builtin_unreachable(); + } +}; + +} diff --git a/dbms/src/Parsers/IAST.h b/dbms/src/Parsers/IAST.h index 5135e08a965..886109c4d61 100644 --- a/dbms/src/Parsers/IAST.h +++ b/dbms/src/Parsers/IAST.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -87,6 +88,26 @@ public: child->dumpTree(ostr, indent + 1); } + void dumpSExpressions(std::ostream & ostr, size_t indent = 0) const + { + String indent_str(indent, ' '); + String id = getID(); + std::replace(id.begin(), id.end(), '_', ' '); + ostr << indent_str << '(' << id; + id = tryGetAlias(); + if (!id.empty()) + ostr << " (Alias " << id << ')'; + if (!children.empty()) + { + ostr << std::endl; + for (const auto & child : children) + child->dumpSExpressions(ostr, indent + 1); + ostr << indent_str << ')' << std::endl; + } + else + ostr << ')' << std::endl; + } + /** Check the depth of the tree. * If max_depth is specified and the depth is greater - throw an exception. * Returns the depth of the tree. diff --git a/dbms/src/Parsers/ParserQueryWithOutput.cpp b/dbms/src/Parsers/ParserQueryWithOutput.cpp index 7c2b6afc442..5d7b75defd9 100644 --- a/dbms/src/Parsers/ParserQueryWithOutput.cpp +++ b/dbms/src/Parsers/ParserQueryWithOutput.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace DB @@ -33,6 +34,11 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec ASTPtr query; + ParserKeyword s_ast("AST"); + bool explain_ast = false; + if (s_ast.ignore(pos, expected)) + explain_ast = true; + bool parsed = select_p.parse(pos, query, expected) || show_tables_p.parse(pos, query, expected) || table_p.parse(pos, query, expected) @@ -74,7 +80,14 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec query_with_output.children.push_back(query_with_output.format); } - node = query; + if (explain_ast) + { + node = std::make_shared(); + node->children.push_back(query); + } + else + node = query; + return true; } From fe8affa1cfc8a6e5da900a5625665beb4d63c744 Mon Sep 17 00:00:00 2001 From: Alexander Krasheninnikov Date: Tue, 25 Sep 2018 03:21:20 +0300 Subject: [PATCH 08/78] Initial --- dbms/src/Common/IXDBCBridgeHelper.cpp | 109 ++++++++++++++++++ dbms/src/Common/IXDBCBridgeHelper.h | 66 +++++++++++ dbms/src/Common/JDBCBridgeHelper.cpp | 30 +++++ dbms/src/Common/JDBCBridgeHelper.h | 27 +++++ .../src/TableFunctions/ITableFunctionXDBC.cpp | 87 ++++++++++++++ dbms/src/TableFunctions/ITableFunctionXDBC.h | 32 +++++ dbms/src/TableFunctions/TableFunctionJDBC.cpp | 20 ++++ dbms/src/TableFunctions/TableFunctionJDBC.h | 27 +++++ 8 files changed, 398 insertions(+) create mode 100644 dbms/src/Common/IXDBCBridgeHelper.cpp create mode 100644 dbms/src/Common/IXDBCBridgeHelper.h create mode 100644 dbms/src/Common/JDBCBridgeHelper.cpp create mode 100644 dbms/src/Common/JDBCBridgeHelper.h create mode 100644 dbms/src/TableFunctions/ITableFunctionXDBC.cpp create mode 100644 dbms/src/TableFunctions/ITableFunctionXDBC.h create mode 100644 dbms/src/TableFunctions/TableFunctionJDBC.cpp create mode 100644 dbms/src/TableFunctions/TableFunctionJDBC.h diff --git a/dbms/src/Common/IXDBCBridgeHelper.cpp b/dbms/src/Common/IXDBCBridgeHelper.cpp new file mode 100644 index 00000000000..794f5a184a6 --- /dev/null +++ b/dbms/src/Common/IXDBCBridgeHelper.cpp @@ -0,0 +1,109 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING; +} + +IXDBCBridgeHelper::IXDBCBridgeHelper( + const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_) + : http_timeout(http_timeout_), connection_string(connection_string_), config(config_) +{ + size_t bridge_port = config.getUInt("odbc_bridge.port", DEFAULT_PORT); + std::string bridge_host = config.getString("odbc_bridge.host", DEFAULT_HOST); + + ping_url.setHost(bridge_host); + ping_url.setPort(bridge_port); + ping_url.setScheme("http"); + ping_url.setPath(PING_HANDLER); +} + + +std::vector> IXDBCBridgeHelper::getURLParams(const std::string & cols, size_t max_block_size) const +{ + std::vector> result; + + result.emplace_back("connection_string", connection_string); /// already validated + result.emplace_back("columns", cols); + result.emplace_back("max_block_size", std::to_string(max_block_size)); + + return result; +} + +bool IXDBCBridgeHelper::checkBridgeIsRunning() const +{ + try + { + ReadWriteBufferFromHTTP buf(ping_url, Poco::Net::HTTPRequest::HTTP_GET, nullptr); + return checkString(IXDBCBridgeHelper::PING_OK_ANSWER, buf); + } + catch (...) + { + return false; + } +} + +void IXDBCBridgeHelper::startBridgeSync() const +{ + if (!checkBridgeIsRunning()) + { + LOG_TRACE(log, "clickhouse-odbc-bridge is not running, will try to start it"); + startBridge(); + bool started = false; + for (size_t counter : ext::range(1, 20)) + { + LOG_TRACE(log, "Checking clickhouse-odbc-bridge is running, try " << counter); + if (checkBridgeIsRunning()) + { + started = true; + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + if (!started) + throw Exception("IXDBCBridgeHelper: clickhouse-odbc-bridge is not responding", ErrorCodes::EXTERNAL_SERVER_IS_NOT_RESPONDING); + } +} + +Poco::URI IXDBCBridgeHelper::getMainURI() const +{ + size_t bridge_port = config.getUInt("odbc_bridge.port", ping_url.getPort()); + std::string bridge_host = config.getString("odbc_bridge.host", ping_url.getHost()); + + Poco::URI main_uri; + main_uri.setHost(bridge_host); + main_uri.setPort(bridge_port); + main_uri.setScheme("http"); + main_uri.setPath(MAIN_HANDLER); + return main_uri; +} + +Poco::URI IXDBCBridgeHelper::getColumnsInfoURI() const +{ + size_t bridge_port = config.getUInt("odbc_bridge.port", ping_url.getPort()); + std::string bridge_host = config.getString("odbc_bridge.host", ping_url.getHost()); + + Poco::URI columns_info_uri; + columns_info_uri.setHost(bridge_host); + columns_info_uri.setPort(bridge_port); + columns_info_uri.setScheme("http"); + columns_info_uri.setPath(COL_INFO_HANDLER); + return columns_info_uri; +} +} diff --git a/dbms/src/Common/IXDBCBridgeHelper.h b/dbms/src/Common/IXDBCBridgeHelper.h new file mode 100644 index 00000000000..b6f7e0b1924 --- /dev/null +++ b/dbms/src/Common/IXDBCBridgeHelper.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int EXTERNAL_EXECUTABLE_NOT_FOUND; +} +/** Base class for Helpers for Xdbc-bridges, provide utility methods, not main request + */ +class IXDBCBridgeHelper +{ +private: + Poco::Timespan http_timeout; + + std::string connection_string; + + Poco::URI ping_url; + + Poco::Logger * log = &Poco::Logger::get("XDBCBridgeHelper"); + +public: + + using Configuration = Poco::Util::AbstractConfiguration; + + const Configuration & config; + + static constexpr inline auto DEFAULT_HOST = "localhost"; + static constexpr inline auto DEFAULT_PORT = 0; + static constexpr inline auto DEFAULT_FORMAT = "RowBinary"; + static constexpr inline auto PING_HANDLER = "/ping"; + static constexpr inline auto MAIN_HANDLER = "/"; + static constexpr inline auto COL_INFO_HANDLER = "/columns_info"; + static constexpr inline auto PING_OK_ANSWER = "Ok."; + + IXDBCBridgeHelper(const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_); + + virtual ~IXDBCBridgeHelper() {} + + std::vector> getURLParams(const std::string & cols, size_t max_block_size) const; + + /* External method for spawning bridge instance */ + void startBridgeSync() const; + + /* Data is fetched via this URI */ + Poco::URI getMainURI() const; + + /* A table schema is inferred via this URI */ + Poco::URI getColumnsInfoURI() const; + +private: + + bool checkBridgeIsRunning() const; + + /* Contains logic for instantiation of the bridge instance */ + virtual void startBridge() const = 0; +}; + + using BridgeHelperPtr = std::shared_ptr; +} \ No newline at end of file diff --git a/dbms/src/Common/JDBCBridgeHelper.cpp b/dbms/src/Common/JDBCBridgeHelper.cpp new file mode 100644 index 00000000000..7c7b9c5e9e1 --- /dev/null +++ b/dbms/src/Common/JDBCBridgeHelper.cpp @@ -0,0 +1,30 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +void JDBCBridgeHelper::startBridge() const { + throw Exception("JDBC bridge does not support automatic instantiation"); +} + +JDBCBridgeHelper::JDBCBridgeHelper(const Configuration & config_, + const Poco::Timespan & http_timeout_, + const std::string & connection_string_) : IXDBCBridgeHelper(config_, http_timeout_, connection_string_), + log(&Logger::get("JDBCBridge")) +{ + log = &Poco::Logger::get("ODBCBridgeHelper"); +} +} diff --git a/dbms/src/Common/JDBCBridgeHelper.h b/dbms/src/Common/JDBCBridgeHelper.h new file mode 100644 index 00000000000..57495953139 --- /dev/null +++ b/dbms/src/Common/JDBCBridgeHelper.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace DB +{ +class JDBCBridgeHelper final : public IXDBCBridgeHelper +{ +private: + + Poco::Logger * log = &Poco::Logger::get("JDBCBridgeHelper"); + +public: + + JDBCBridgeHelper(const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_); + +private: + + /* Contains logic for instantiation of the bridge instance */ + void startBridge() const override; +}; + +} \ No newline at end of file diff --git a/dbms/src/TableFunctions/ITableFunctionXDBC.cpp b/dbms/src/TableFunctions/ITableFunctionXDBC.cpp new file mode 100644 index 00000000000..dbcbed69db4 --- /dev/null +++ b/dbms/src/TableFunctions/ITableFunctionXDBC.cpp @@ -0,0 +1,87 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + namespace ErrorCodes + { + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; + } + + StoragePtr ITableFunctionXDBC::executeImpl(const ASTPtr & ast_function, const Context & context) const + { + const ASTFunction & args_func = typeid_cast(*ast_function); + + if (!args_func.arguments) + throw Exception("Table function '" + getName() + "' must have arguments.", ErrorCodes::LOGICAL_ERROR); + + ASTs & args = typeid_cast(*args_func.arguments).children; + if (args.size() != 2 && args.size() != 3) + throw Exception("Table function '" + getName() + "' requires 2 or 3 arguments: ODBC('DSN', table) or ODBC('DSN', schema, table)", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + for (auto i = 0u; i < args.size(); ++i) + args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(args[i], context); + + std::string connection_string = ""; + std::string schema_name = ""; + std::string table_name = ""; + if (args.size() == 3) + { + connection_string = static_cast(*args[0]).value.safeGet(); + schema_name = static_cast(*args[1]).value.safeGet(); + table_name = static_cast(*args[2]).value.safeGet(); + } else if (args.size() == 2) + { + connection_string = static_cast(*args[0]).value.safeGet(); + table_name = static_cast(*args[1]).value.safeGet(); + } + + const auto & config = context.getConfigRef(); + + /* Infer external table structure */ + BridgeHelperPtr helper = createBridgeHelper(config, context.getSettingsRef().http_receive_timeout.value, connection_string); + helper->startBridgeSync(); + + Poco::URI columns_info_uri = helper->getColumnsInfoURI(); + columns_info_uri.addQueryParameter("connection_string", connection_string); + if (!schema_name.empty()) + columns_info_uri.addQueryParameter("schema", schema_name); + columns_info_uri.addQueryParameter("table", table_name); + + ReadWriteBufferFromHTTP buf(columns_info_uri, Poco::Net::HTTPRequest::HTTP_POST, nullptr); + + std::string columns_info; + readStringBinary(columns_info, buf); + NamesAndTypesList columns = NamesAndTypesList::parse(columns_info); + + auto result = createStorage(table_name, connection_string, schema_name, table_name, ColumnsDescription{columns}, context); + + if(!result) + throw Exception("Failed to instantiate storage from table function " + getName()) + + result->startup(); + return result; + } + +} diff --git a/dbms/src/TableFunctions/ITableFunctionXDBC.h b/dbms/src/TableFunctions/ITableFunctionXDBC.h new file mode 100644 index 00000000000..e03a0ebf029 --- /dev/null +++ b/dbms/src/TableFunctions/ITableFunctionXDBC.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include + +namespace DB +{ +/** + * Base class for table functions, that works over external bridge + * Xdbc (Xdbc connect string, table) - creates a temporary StorageXDBC. + */ +class ITableFunctionXDBC : public ITableFunction +{ +private: + StoragePtr executeImpl(const ASTPtr & ast_function, const Context & context) const override; + + /* A factory method for creating the storage implementation */ + virtual StoragePtr createStorage(const std::string & table_name_, + const std::string & connection_string, + const std::string & remote_database_name, + const std::string & remote_table_name, + const ColumnsDescription & columns_, + const Context & context_) const = 0; + + /* A factory method to create bridge helper, that will assist in remote interaction */ + virtual BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, + const Poco::Timespan & http_timeout_, + const std::string & connection_string_) const = 0; +}; +} diff --git a/dbms/src/TableFunctions/TableFunctionJDBC.cpp b/dbms/src/TableFunctions/TableFunctionJDBC.cpp new file mode 100644 index 00000000000..141dd9be6a1 --- /dev/null +++ b/dbms/src/TableFunctions/TableFunctionJDBC.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + + +namespace DB +{ + StoragePtr TableFunctionJDBC::createStorage(const std::string & , const std::string & , + const std::string & , const std::string & , + const ColumnsDescription & , const Context & ) const { + return nullptr; + } + + + BridgeHelperPtr TableFunctionJDBC::createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, + const Poco::Timespan & http_timeout_, + const std::string & connection_string_) const { + return std::make_shared(config_, http_timeout_, connection_string_); + } +} diff --git a/dbms/src/TableFunctions/TableFunctionJDBC.h b/dbms/src/TableFunctions/TableFunctionJDBC.h new file mode 100644 index 00000000000..402d6964cda --- /dev/null +++ b/dbms/src/TableFunctions/TableFunctionJDBC.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace DB +{ +/* jdbc (odbc connect string, table) - creates a temporary StorageJDBC. + */ +class TableFunctionJDBC : public ITableFunctionXDBC +{ +public: + static constexpr auto name = "jdbc"; + std::string getName() const override + { + return name; + } +private: + StoragePtr createStorage(const std::string &table_name_, const std::string &connection_string, + const std::string &remote_database_name, const std::string &remote_table_name, + const ColumnsDescription &columns_, const Context &context_) const override; + + BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, + const Poco::Timespan & http_timeout_, + const std::string & connection_string_) const override; +}; +} From 647d1041f1fe3c987d2ddee71dc753e2281a6242 Mon Sep 17 00:00:00 2001 From: Alexandr Krasheninnikov Date: Tue, 25 Sep 2018 17:29:44 +0300 Subject: [PATCH 09/78] Implement jdbc/idbc bridge connectors --- dbms/src/Common/IXDBCBridgeHelper.cpp | 109 ------- dbms/src/Common/IXDBCBridgeHelper.h | 66 ---- dbms/src/Common/JDBCBridgeHelper.cpp | 30 -- dbms/src/Common/JDBCBridgeHelper.h | 27 -- dbms/src/Common/XDBCBridgeHelper.h | 297 ++++++++++++++++++ dbms/src/Storages/StorageXDBC.cpp | 153 +++++++++ dbms/src/Storages/StorageXDBC.h | 90 ++++++ dbms/src/Storages/registerStorages.cpp | 6 + .../src/TableFunctions/ITableFunctionXDBC.cpp | 15 +- dbms/src/TableFunctions/ITableFunctionXDBC.h | 58 +++- dbms/src/TableFunctions/TableFunctionJDBC.cpp | 20 -- dbms/src/TableFunctions/TableFunctionJDBC.h | 27 -- .../TableFunctions/registerTableFunctions.cpp | 5 + 13 files changed, 618 insertions(+), 285 deletions(-) delete mode 100644 dbms/src/Common/IXDBCBridgeHelper.cpp delete mode 100644 dbms/src/Common/IXDBCBridgeHelper.h delete mode 100644 dbms/src/Common/JDBCBridgeHelper.cpp delete mode 100644 dbms/src/Common/JDBCBridgeHelper.h create mode 100644 dbms/src/Common/XDBCBridgeHelper.h create mode 100644 dbms/src/Storages/StorageXDBC.cpp create mode 100644 dbms/src/Storages/StorageXDBC.h delete mode 100644 dbms/src/TableFunctions/TableFunctionJDBC.cpp delete mode 100644 dbms/src/TableFunctions/TableFunctionJDBC.h diff --git a/dbms/src/Common/IXDBCBridgeHelper.cpp b/dbms/src/Common/IXDBCBridgeHelper.cpp deleted file mode 100644 index 794f5a184a6..00000000000 --- a/dbms/src/Common/IXDBCBridgeHelper.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING; -} - -IXDBCBridgeHelper::IXDBCBridgeHelper( - const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_) - : http_timeout(http_timeout_), connection_string(connection_string_), config(config_) -{ - size_t bridge_port = config.getUInt("odbc_bridge.port", DEFAULT_PORT); - std::string bridge_host = config.getString("odbc_bridge.host", DEFAULT_HOST); - - ping_url.setHost(bridge_host); - ping_url.setPort(bridge_port); - ping_url.setScheme("http"); - ping_url.setPath(PING_HANDLER); -} - - -std::vector> IXDBCBridgeHelper::getURLParams(const std::string & cols, size_t max_block_size) const -{ - std::vector> result; - - result.emplace_back("connection_string", connection_string); /// already validated - result.emplace_back("columns", cols); - result.emplace_back("max_block_size", std::to_string(max_block_size)); - - return result; -} - -bool IXDBCBridgeHelper::checkBridgeIsRunning() const -{ - try - { - ReadWriteBufferFromHTTP buf(ping_url, Poco::Net::HTTPRequest::HTTP_GET, nullptr); - return checkString(IXDBCBridgeHelper::PING_OK_ANSWER, buf); - } - catch (...) - { - return false; - } -} - -void IXDBCBridgeHelper::startBridgeSync() const -{ - if (!checkBridgeIsRunning()) - { - LOG_TRACE(log, "clickhouse-odbc-bridge is not running, will try to start it"); - startBridge(); - bool started = false; - for (size_t counter : ext::range(1, 20)) - { - LOG_TRACE(log, "Checking clickhouse-odbc-bridge is running, try " << counter); - if (checkBridgeIsRunning()) - { - started = true; - break; - } - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - if (!started) - throw Exception("IXDBCBridgeHelper: clickhouse-odbc-bridge is not responding", ErrorCodes::EXTERNAL_SERVER_IS_NOT_RESPONDING); - } -} - -Poco::URI IXDBCBridgeHelper::getMainURI() const -{ - size_t bridge_port = config.getUInt("odbc_bridge.port", ping_url.getPort()); - std::string bridge_host = config.getString("odbc_bridge.host", ping_url.getHost()); - - Poco::URI main_uri; - main_uri.setHost(bridge_host); - main_uri.setPort(bridge_port); - main_uri.setScheme("http"); - main_uri.setPath(MAIN_HANDLER); - return main_uri; -} - -Poco::URI IXDBCBridgeHelper::getColumnsInfoURI() const -{ - size_t bridge_port = config.getUInt("odbc_bridge.port", ping_url.getPort()); - std::string bridge_host = config.getString("odbc_bridge.host", ping_url.getHost()); - - Poco::URI columns_info_uri; - columns_info_uri.setHost(bridge_host); - columns_info_uri.setPort(bridge_port); - columns_info_uri.setScheme("http"); - columns_info_uri.setPath(COL_INFO_HANDLER); - return columns_info_uri; -} -} diff --git a/dbms/src/Common/IXDBCBridgeHelper.h b/dbms/src/Common/IXDBCBridgeHelper.h deleted file mode 100644 index b6f7e0b1924..00000000000 --- a/dbms/src/Common/IXDBCBridgeHelper.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int EXTERNAL_EXECUTABLE_NOT_FOUND; -} -/** Base class for Helpers for Xdbc-bridges, provide utility methods, not main request - */ -class IXDBCBridgeHelper -{ -private: - Poco::Timespan http_timeout; - - std::string connection_string; - - Poco::URI ping_url; - - Poco::Logger * log = &Poco::Logger::get("XDBCBridgeHelper"); - -public: - - using Configuration = Poco::Util::AbstractConfiguration; - - const Configuration & config; - - static constexpr inline auto DEFAULT_HOST = "localhost"; - static constexpr inline auto DEFAULT_PORT = 0; - static constexpr inline auto DEFAULT_FORMAT = "RowBinary"; - static constexpr inline auto PING_HANDLER = "/ping"; - static constexpr inline auto MAIN_HANDLER = "/"; - static constexpr inline auto COL_INFO_HANDLER = "/columns_info"; - static constexpr inline auto PING_OK_ANSWER = "Ok."; - - IXDBCBridgeHelper(const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_); - - virtual ~IXDBCBridgeHelper() {} - - std::vector> getURLParams(const std::string & cols, size_t max_block_size) const; - - /* External method for spawning bridge instance */ - void startBridgeSync() const; - - /* Data is fetched via this URI */ - Poco::URI getMainURI() const; - - /* A table schema is inferred via this URI */ - Poco::URI getColumnsInfoURI() const; - -private: - - bool checkBridgeIsRunning() const; - - /* Contains logic for instantiation of the bridge instance */ - virtual void startBridge() const = 0; -}; - - using BridgeHelperPtr = std::shared_ptr; -} \ No newline at end of file diff --git a/dbms/src/Common/JDBCBridgeHelper.cpp b/dbms/src/Common/JDBCBridgeHelper.cpp deleted file mode 100644 index 7c7b9c5e9e1..00000000000 --- a/dbms/src/Common/JDBCBridgeHelper.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace DB -{ - -void JDBCBridgeHelper::startBridge() const { - throw Exception("JDBC bridge does not support automatic instantiation"); -} - -JDBCBridgeHelper::JDBCBridgeHelper(const Configuration & config_, - const Poco::Timespan & http_timeout_, - const std::string & connection_string_) : IXDBCBridgeHelper(config_, http_timeout_, connection_string_), - log(&Logger::get("JDBCBridge")) -{ - log = &Poco::Logger::get("ODBCBridgeHelper"); -} -} diff --git a/dbms/src/Common/JDBCBridgeHelper.h b/dbms/src/Common/JDBCBridgeHelper.h deleted file mode 100644 index 57495953139..00000000000 --- a/dbms/src/Common/JDBCBridgeHelper.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace DB -{ -class JDBCBridgeHelper final : public IXDBCBridgeHelper -{ -private: - - Poco::Logger * log = &Poco::Logger::get("JDBCBridgeHelper"); - -public: - - JDBCBridgeHelper(const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_); - -private: - - /* Contains logic for instantiation of the bridge instance */ - void startBridge() const override; -}; - -} \ No newline at end of file diff --git a/dbms/src/Common/XDBCBridgeHelper.h b/dbms/src/Common/XDBCBridgeHelper.h new file mode 100644 index 00000000000..3e34ad80189 --- /dev/null +++ b/dbms/src/Common/XDBCBridgeHelper.h @@ -0,0 +1,297 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int EXTERNAL_EXECUTABLE_NOT_FOUND; + extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING; +} + +/** + * Class for Helpers for Xdbc-bridges, provide utility methods, not main request + */ +class IXDBCBridgeHelper +{ +public: + static constexpr inline auto DEFAULT_FORMAT = "RowBinary"; + + virtual std::vector> getURLParams(const std::string & cols, size_t max_block_size) const = 0; + virtual void startBridgeSync() const = 0; + virtual Poco::URI getMainURI() const = 0; + virtual Poco::URI getColumnsInfoURI() const = 0; + virtual IdentifierQuotingStyle getQuotingStyle() const = 0; + + virtual ~IXDBCBridgeHelper() {} +}; + +using BridgeHelperPtr = std::shared_ptr; + +template +class XDBCBridgeHelper : public IXDBCBridgeHelper +{ +private: + Poco::Timespan http_timeout; + + std::string connection_string; + + Poco::URI ping_url; + + Poco::Logger * log = &Poco::Logger::get(BridgeHelperMixin::NAME); + +protected: + auto getConnectionString() const + { + return connection_string; + } + +public: + + using Configuration = Poco::Util::AbstractConfiguration; + + const Configuration & config; + + static constexpr inline auto DEFAULT_HOST = "localhost"; + static constexpr inline auto DEFAULT_PORT = BridgeHelperMixin::DEFAULT_PORT; + static constexpr inline auto PING_HANDLER = "/ping"; + static constexpr inline auto MAIN_HANDLER = "/"; + static constexpr inline auto COL_INFO_HANDLER = "/columns_info"; + static constexpr inline auto PING_OK_ANSWER = "Ok."; + + XDBCBridgeHelper( + const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_) + : http_timeout(http_timeout_), connection_string(connection_string_), config(config_) + { + size_t bridge_port = config.getUInt(BridgeHelperMixin::configPrefix() + ".port", DEFAULT_PORT); + std::string bridge_host = config.getString(BridgeHelperMixin::configPrefix() + ".host", DEFAULT_HOST); + + ping_url.setHost(bridge_host); + ping_url.setPort(bridge_port); + ping_url.setScheme("http"); + ping_url.setPath(PING_HANDLER); + } + + virtual ~XDBCBridgeHelper() {} + + IdentifierQuotingStyle getQuotingStyle() const override + { + return BridgeHelperMixin::getQuotingStyle(); + } + + /** + * @todo leaky abstraction - used by external API's + */ + std::vector> getURLParams(const std::string & cols, size_t max_block_size) const override + { + std::vector> result; + + result.emplace_back("connection_string", connection_string); /// already validated + result.emplace_back("columns", cols); + result.emplace_back("max_block_size", std::to_string(max_block_size)); + + return result; + } + + /** + * Performs spawn of external daemon + */ + void startBridgeSync() const override + { + if (!checkBridgeIsRunning()) + { + LOG_TRACE(log, BridgeHelperMixin::serviceAlias() + " is not running, will try to start it"); + startBridge(); + bool started = false; + for (size_t counter : ext::range(1, 20)) + { + LOG_TRACE(log, "Checking " + BridgeHelperMixin::serviceAlias() + " is running, try " << counter); + if (checkBridgeIsRunning()) + { + started = true; + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + if (!started) + throw Exception("XDBCBridgeHelper: " + BridgeHelperMixin::serviceAlias() + " is not responding", ErrorCodes::EXTERNAL_SERVER_IS_NOT_RESPONDING); + } + } + + /** + * URI to fetch the data from external service + */ + Poco::URI getMainURI() const override + { + auto uri = createBaseURI(); + uri.setPath(MAIN_HANDLER); + return uri; + } + + /** + * URI to retrieve column description from external service + */ + Poco::URI getColumnsInfoURI() const override + { + auto uri = createBaseURI(); + uri.setPath(COL_INFO_HANDLER); + return uri; + } + +protected: + Poco::URI createBaseURI() const + { + size_t bridge_port = config.getUInt(BridgeHelperMixin::serviceAlias()+ ".port", DEFAULT_PORT); + std::string bridge_host = config.getString(BridgeHelperMixin::serviceAlias() + ".host", DEFAULT_HOST); + + Poco::URI uri; + uri.setHost(bridge_host); + uri.setPort(bridge_port); + uri.setScheme("http"); + return uri; + } + +private: + + + bool checkBridgeIsRunning() const + { + try + { + ReadWriteBufferFromHTTP buf(ping_url, Poco::Net::HTTPRequest::HTTP_GET, nullptr); + return checkString(XDBCBridgeHelper::PING_OK_ANSWER, buf); + } + catch (...) + { + return false; + } + } + + /* Contains logic for instantiation of the bridge instance */ + void startBridge() const + { + BridgeHelperMixin::startBridge(config, log, http_timeout); + } +}; + +struct JDBCBridgeMixin +{ + static constexpr inline auto DEFAULT_PORT = 9019; + static constexpr inline auto NAME = "JDBCBridgeHelper"; + static const String configPrefix() { return "jdbc_bridge"; } + static const String serviceAlias() { return "clickhouse-jdbc-bridge"; } + + static void startBridge(const Poco::Util::AbstractConfiguration & , const Poco::Logger * , const Poco::Timespan & ) + { + throw Exception("jdbc-bridge does not support external auto-start"); + } + + static IdentifierQuotingStyle getQuotingStyle() { + throw Exception("This method should not be called directly"); + } +}; + +class JDBCBridgeHelper : public XDBCBridgeHelper +{ + static constexpr inline auto QUOTE_STYLE_HANDLER = "/quote_style"; + +public: + + JDBCBridgeHelper( + const Poco::Util::AbstractConfiguration & config_, + const Poco::Timespan & http_timeout_, + const std::string & connection_string_) + : XDBCBridgeHelper(config_, http_timeout_, connection_string_) {} + + IdentifierQuotingStyle getQuotingStyle() const override { + + auto uri = createBaseURI(); + uri.setPath(QUOTE_STYLE_HANDLER); + uri.addQueryParameter("connection_string", getConnectionString()); + + ReadWriteBufferFromHTTP buf(uri, Poco::Net::HTTPRequest::HTTP_GET, nullptr); + std::string character; + readStringBinary(character, buf); + if (character.length() > 1) + throw Exception("Failed to get quoting style from jdbc-bridge"); + + if(character[0] == '`') + return IdentifierQuotingStyle::Backticks; + else if(character[0] == '"') + return IdentifierQuotingStyle::DoubleQuotes; + else + throw Exception("Failed to determine quoting style from jdbc-bridge response: " + character); + } +}; + +struct ODBCBridgeMixin { + + static constexpr inline auto DEFAULT_PORT = 9018; + static constexpr inline auto NAME = "ODBCBridgeHelper"; + + static const String configPrefix() { return "odbc_bridge"; } + static const String serviceAlias() { return "clickhouse-odbc-bridge"; } + + static IdentifierQuotingStyle getQuotingStyle() { return IdentifierQuotingStyle::DoubleQuotes; } + + static void startBridge(const Poco::Util::AbstractConfiguration & config, const Poco::Logger * , const Poco::Timespan & http_timeout) + { + Poco::Path path{config.getString("application.dir", "")}; + + path.setFileName( +#if CLICKHOUSE_SPLIT_BINARY + "clickhouse-odbc-bridge" +#else + "clickhouse" +#endif + ); + + if (!Poco::File(path).exists()) + throw Exception("clickhouse binary (" + path.toString() + ") is not found", ErrorCodes::EXTERNAL_EXECUTABLE_NOT_FOUND); + + std::stringstream command; + + command << path.toString() << + #if CLICKHOUSE_SPLIT_BINARY + " " + #else + " odbc-bridge " +#endif + ; + + command << "--http-port " << config.getUInt(configPrefix() + ".port", DEFAULT_PORT) << ' '; + command << "--listen-host " << config.getString(configPrefix() + ".listen_host", XDBCBridgeHelper::DEFAULT_HOST) << ' '; + command << "--http-timeout " << http_timeout.totalMicroseconds() << ' '; + if (config.has("logger.odbc_bridge_log")) + command << "--log-path " << config.getString("logger."+configPrefix()+"_log") << ' '; + if (config.has("logger.odbc_bridge_errlog")) + command << "--err-log-path " << config.getString("logger." + configPrefix() + "_errlog") << ' '; + if (config.has("logger.odbc_bridge_level")) + command << "--log-level " << config.getString("logger." + configPrefix() + "_level") << ' '; + command << "&"; /// we don't want to wait this process + + auto command_str = command.str(); +// LOG_TRACE(log, "Starting " + serviceAlias() +" with command: " << command_str); + + auto cmd = ShellCommand::execute(command_str); + cmd->wait(); + } +}; + +} \ No newline at end of file diff --git a/dbms/src/Storages/StorageXDBC.cpp b/dbms/src/Storages/StorageXDBC.cpp new file mode 100644 index 00000000000..ec72e9594db --- /dev/null +++ b/dbms/src/Storages/StorageXDBC.cpp @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +namespace DB +{ + namespace ErrorCodes + { + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; + extern const int EXTERNAL_EXECUTABLE_NOT_FOUND; + extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING; + } + + + StorageXDBC::StorageXDBC(const std::string & table_name_, + const std::string & remote_database_name_, + const std::string & remote_table_name_, + const ColumnsDescription & columns_, + const Context & context_, + const BridgeHelperPtr bridge_helper_) + : IStorageURLBase(Poco::URI(), context_, table_name_, IXDBCBridgeHelper::DEFAULT_FORMAT, columns_) + , bridge_helper(bridge_helper_) + , remote_database_name(remote_database_name_) + , remote_table_name(remote_table_name_) + { + // @todo give it appropriate name + log = &Poco::Logger::get("StorageXDBC"); + uri = bridge_helper->getMainURI(); + } + + std::string StorageXDBC::getReadMethod() const + { + return Poco::Net::HTTPRequest::HTTP_POST; + } + + std::vector> StorageXDBC::getReadURIParams(const Names & column_names, + const SelectQueryInfo & /*query_info*/, + const Context & /*context*/, + QueryProcessingStage::Enum & /*processed_stage*/, + size_t max_block_size) const + { + NamesAndTypesList cols; + for (const String & name : column_names) + { + auto column_data = getColumn(name); + cols.emplace_back(column_data.name, column_data.type); + } + return bridge_helper->getURLParams(cols.toString(), max_block_size); + } + + std::function StorageXDBC::getReadPOSTDataCallback(const Names & /*column_names*/, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum & /*processed_stage*/, + size_t /*max_block_size*/) const + { + String query = transformQueryForExternalDatabase( + *query_info.query, getColumns().ordinary, bridge_helper->getQuotingStyle(), remote_database_name, remote_table_name, context); + + return [query](std::ostream & os) { os << "query=" << query; }; + } + + BlockInputStreams StorageXDBC::read(const Names & column_names, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum processed_stage, + size_t max_block_size, + unsigned num_streams) + { + check(column_names); + + bridge_helper->startBridgeSync(); + return IStorageURLBase::read(column_names, query_info, context, processed_stage, max_block_size, num_streams); + } + + + Block StorageXDBC::getHeaderBlock(const Names & column_names) const + { + return getSampleBlockForColumns(column_names); + } + + void registerStorageJDBC(StorageFactory & factory) + { + factory.registerStorage("JDBC", [](const StorageFactory::Arguments & args) + { + ASTs & engine_args = args.engine_args; + + if (engine_args.size() != 3) + throw Exception( + "Storage JDBC requires exactly 3 parameters: JDBC('DSN', database or schema, table)", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + for (size_t i = 0; i < 3; ++i) + engine_args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[i], args.local_context); + + BridgeHelperPtr bridge_helper = std::make_shared( + args.context.getConfigRef(), + args.context.getSettingsRef().http_receive_timeout.value, + static_cast(*engine_args[0]).value.safeGet() + ); + return std::make_shared(args.table_name, + static_cast(*engine_args[1]).value.safeGet(), + static_cast(*engine_args[2]).value.safeGet(), + args.columns, + args.context, + bridge_helper + ); + + }); + } + + void registerStorageIDBC(StorageFactory & factory) + { + factory.registerStorage("IDBC", [](const StorageFactory::Arguments & args) + { + ASTs & engine_args = args.engine_args; + + if (engine_args.size() != 3) + throw Exception( + "Storage IDBC requires exactly 3 parameters: IDBC('DSN', database or schema, table)", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + for (size_t i = 0; i < 3; ++i) + engine_args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[i], args.local_context); + + BridgeHelperPtr bridge_helper = std::make_shared>( + args.context.getConfigRef(), + args.context.getSettingsRef().http_receive_timeout.value, + static_cast(*engine_args[0]).value.safeGet() + ); + return std::make_shared(args.table_name, + static_cast(*engine_args[1]).value.safeGet(), + static_cast(*engine_args[2]).value.safeGet(), + args.columns, + args.context, + bridge_helper + ); + + }); + } + +} diff --git a/dbms/src/Storages/StorageXDBC.h b/dbms/src/Storages/StorageXDBC.h new file mode 100644 index 00000000000..8a522d33ec7 --- /dev/null +++ b/dbms/src/Storages/StorageXDBC.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include +#include + +namespace DB +{ +/** Implements storage in the XDBC database. + * Use ENGINE = xdbc(connection_string, table_name) + * Example ENGINE = odbc('dsn=test', table) + * Read only. + */ + class StorageXDBC : public IStorageURLBase + { + public: + + BlockInputStreams read(const Names & column_names, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum processed_stage, + size_t max_block_size, + unsigned num_streams) override; + + + StorageXDBC(const std::string & table_name_, + const std::string & remote_database_name, + const std::string & remote_table_name, + const ColumnsDescription & columns_, + const Context & context_, BridgeHelperPtr bridge_helper_); + + private: + + BridgeHelperPtr bridge_helper; + std::string remote_database_name; + std::string remote_table_name; + + Poco::Logger * log; + + std::string getReadMethod() const override; + + std::vector> getReadURIParams(const Names & column_names, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum & processed_stage, + size_t max_block_size) const override; + + std::function getReadPOSTDataCallback(const Names & column_names, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum & processed_stage, + size_t max_block_size) const override; + + Block getHeaderBlock(const Names & column_names) const override; + }; + + // Implementations for StorageXDBC + class StorageJDBC : public StorageXDBC + { + public: + StorageJDBC(const std::string & table_name_, + const std::string & remote_database_name, + const std::string & remote_table_name, + const ColumnsDescription & columns_, + const Context & context_, BridgeHelperPtr bridge_helper_) + : StorageXDBC(table_name_, remote_database_name, remote_table_name, columns_, context_, bridge_helper_) {} + + std::string getName() const override + { + return "JDBC"; + } + }; + + class StorageIDBC : public StorageXDBC + { + public: + StorageIDBC(const std::string & table_name_, + const std::string & remote_database_name, + const std::string & remote_table_name, + const ColumnsDescription & columns_, + const Context & context_, BridgeHelperPtr bridge_helper_) + : StorageXDBC(table_name_, remote_database_name, remote_table_name, columns_, context_, bridge_helper_) {} + + std::string getName() const override + { + return "IDBC"; + } + }; +} diff --git a/dbms/src/Storages/registerStorages.cpp b/dbms/src/Storages/registerStorages.cpp index 182a3f8016a..eef34aa0c82 100644 --- a/dbms/src/Storages/registerStorages.cpp +++ b/dbms/src/Storages/registerStorages.cpp @@ -26,8 +26,11 @@ void registerStorageMaterializedView(StorageFactory & factory); #if USE_POCO_SQLODBC || USE_POCO_DATAODBC void registerStorageODBC(StorageFactory & factory); +void registerStorageIDBC(StorageFactory & factory); #endif +void registerStorageJDBC(StorageFactory & factory); + #if USE_MYSQL void registerStorageMySQL(StorageFactory & factory); #endif @@ -60,7 +63,10 @@ void registerStorages() #if USE_POCO_SQLODBC || USE_POCO_DATAODBC registerStorageODBC(factory); + registerStorageIDBC(factory); #endif + registerStorageJDBC(factory); + #if USE_MYSQL registerStorageMySQL(factory); diff --git a/dbms/src/TableFunctions/ITableFunctionXDBC.cpp b/dbms/src/TableFunctions/ITableFunctionXDBC.cpp index dbcbed69db4..ce7e02a4071 100644 --- a/dbms/src/TableFunctions/ITableFunctionXDBC.cpp +++ b/dbms/src/TableFunctions/ITableFunctionXDBC.cpp @@ -37,7 +37,7 @@ namespace DB ASTs & args = typeid_cast(*args_func.arguments).children; if (args.size() != 2 && args.size() != 3) - throw Exception("Table function '" + getName() + "' requires 2 or 3 arguments: ODBC('DSN', table) or ODBC('DSN', schema, table)", + throw Exception("Table function '" + getName() + "' requires 2 or 3 arguments: " + getName() + "('DSN', table) or "+getName()+"('DSN', schema, table)", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); for (auto i = 0u; i < args.size(); ++i) @@ -75,13 +75,22 @@ namespace DB readStringBinary(columns_info, buf); NamesAndTypesList columns = NamesAndTypesList::parse(columns_info); - auto result = createStorage(table_name, connection_string, schema_name, table_name, ColumnsDescription{columns}, context); + auto result = createStorage(table_name, schema_name, table_name, ColumnsDescription{columns}, context, helper); if(!result) - throw Exception("Failed to instantiate storage from table function " + getName()) + throw Exception("Failed to instantiate storage from table function " + getName()); result->startup(); return result; } + void registerTableFunctionJDBC(TableFunctionFactory & factory) + { + factory.registerFunction(); + } + + void registerTableFunctionIDBC(TableFunctionFactory & factory) + { + factory.registerFunction(); + } } diff --git a/dbms/src/TableFunctions/ITableFunctionXDBC.h b/dbms/src/TableFunctions/ITableFunctionXDBC.h index e03a0ebf029..8b46453aeea 100644 --- a/dbms/src/TableFunctions/ITableFunctionXDBC.h +++ b/dbms/src/TableFunctions/ITableFunctionXDBC.h @@ -1,9 +1,10 @@ #pragma once #include -#include +#include #include #include +#include namespace DB { @@ -18,15 +19,66 @@ private: /* A factory method for creating the storage implementation */ virtual StoragePtr createStorage(const std::string & table_name_, - const std::string & connection_string, const std::string & remote_database_name, const std::string & remote_table_name, const ColumnsDescription & columns_, - const Context & context_) const = 0; + const Context & context_, + BridgeHelperPtr bridge_helper) const = 0; /* A factory method to create bridge helper, that will assist in remote interaction */ virtual BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_) const = 0; }; + +class TableFunctionJDBC : public ITableFunctionXDBC +{ +public: + static constexpr auto name = "jdbc"; + std::string getName() const override + { + return name; + } +private: + StoragePtr createStorage(const std::string &table_name_, const std::string &remote_database_name, + const std::string &remote_table_name, + const ColumnsDescription &columns_, + const Context &context_, BridgeHelperPtr bridge_helper) const override { + + return std::make_shared(table_name_, remote_database_name, remote_table_name, columns_, context_, bridge_helper); + } + + + BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, + const Poco::Timespan & http_timeout_, + const std::string & connection_string_) const override { + return std::make_shared(config_, http_timeout_, connection_string_); + } +}; + +class TableFunctionIDBC : public ITableFunctionXDBC +{ +public: + static constexpr auto name = "idbc"; + std::string getName() const override + { + return name; + } +private: + StoragePtr createStorage(const std::string &table_name_, const std::string &remote_database_name, + const std::string &remote_table_name, + const ColumnsDescription &columns_, + const Context &context_, BridgeHelperPtr bridge_helper) const override { + + return std::make_shared(table_name_, remote_database_name, remote_table_name, columns_, context_, bridge_helper); + } + + + BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, + const Poco::Timespan & http_timeout_, + const std::string & connection_string_) const override { + return std::make_shared>(config_, http_timeout_, connection_string_); + } +}; + } diff --git a/dbms/src/TableFunctions/TableFunctionJDBC.cpp b/dbms/src/TableFunctions/TableFunctionJDBC.cpp deleted file mode 100644 index 141dd9be6a1..00000000000 --- a/dbms/src/TableFunctions/TableFunctionJDBC.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include - - -namespace DB -{ - StoragePtr TableFunctionJDBC::createStorage(const std::string & , const std::string & , - const std::string & , const std::string & , - const ColumnsDescription & , const Context & ) const { - return nullptr; - } - - - BridgeHelperPtr TableFunctionJDBC::createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, - const Poco::Timespan & http_timeout_, - const std::string & connection_string_) const { - return std::make_shared(config_, http_timeout_, connection_string_); - } -} diff --git a/dbms/src/TableFunctions/TableFunctionJDBC.h b/dbms/src/TableFunctions/TableFunctionJDBC.h deleted file mode 100644 index 402d6964cda..00000000000 --- a/dbms/src/TableFunctions/TableFunctionJDBC.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include - -namespace DB -{ -/* jdbc (odbc connect string, table) - creates a temporary StorageJDBC. - */ -class TableFunctionJDBC : public ITableFunctionXDBC -{ -public: - static constexpr auto name = "jdbc"; - std::string getName() const override - { - return name; - } -private: - StoragePtr createStorage(const std::string &table_name_, const std::string &connection_string, - const std::string &remote_database_name, const std::string &remote_table_name, - const ColumnsDescription &columns_, const Context &context_) const override; - - BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, - const Poco::Timespan & http_timeout_, - const std::string & connection_string_) const override; -}; -} diff --git a/dbms/src/TableFunctions/registerTableFunctions.cpp b/dbms/src/TableFunctions/registerTableFunctions.cpp index 8e2128fe908..71ce092b398 100644 --- a/dbms/src/TableFunctions/registerTableFunctions.cpp +++ b/dbms/src/TableFunctions/registerTableFunctions.cpp @@ -16,8 +16,11 @@ void registerTableFunctionURL(TableFunctionFactory & factory); #if USE_POCO_SQLODBC || USE_POCO_DATAODBC void registerTableFunctionODBC(TableFunctionFactory & factory); +void registerTableFunctionIDBC(TableFunctionFactory & factory); #endif +void registerTableFunctionJDBC(TableFunctionFactory & factory); + #if USE_MYSQL void registerTableFunctionMySQL(TableFunctionFactory & factory); #endif @@ -37,7 +40,9 @@ void registerTableFunctions() #if USE_POCO_SQLODBC || USE_POCO_DATAODBC registerTableFunctionODBC(factory); + registerTableFunctionIDBC(factory); #endif + registerTableFunctionJDBC(factory); #if USE_MYSQL registerTableFunctionMySQL(factory); From 3e96f32833a3e9c335f6399dd400baf64d8c7101 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 26 Sep 2018 11:27:04 +0000 Subject: [PATCH 10/78] Fixed the bug introduced by PR#3144 The pre-existing optimization in the function JSONEachRowRowInputStream::readColumnName() (that was extracted during the refactoring step of PR#3144) imposed a restriction on its usage - reading from the input stream might invalidate the return value of that function, and this is what happenned in readJSONObject() after the call to skipColonDelimiter(). One way of fixing the problem while preserving the original optimization intact would be to defer the call to skipColonDelimiter() until the variable name_ref was fully consumed, however that would result in worse code (skipColonDelimiter() would need to be called in three different places where it doesn't really belong). Therefore I preferred to slightly weaken the optimization by always copying the key name into the current_column_name data member. --- dbms/src/Formats/JSONEachRowRowInputStream.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dbms/src/Formats/JSONEachRowRowInputStream.cpp b/dbms/src/Formats/JSONEachRowRowInputStream.cpp index 8da319be00b..8d7f717fca1 100644 --- a/dbms/src/Formats/JSONEachRowRowInputStream.cpp +++ b/dbms/src/Formats/JSONEachRowRowInputStream.cpp @@ -68,7 +68,8 @@ size_t JSONEachRowRowInputStream::columnIndex(const StringRef& name) const */ StringRef JSONEachRowRowInputStream::readColumnName(ReadBuffer & buf) { - // This is just an optimization: try to avoid copying the name into current_column_name + // This is just an optimization: try to avoid calling readJSONStringInto() + if (nested_prefix_length == 0 && buf.position() + 1 < buf.buffer().end()) { const char * next_pos = find_first_symbols<'\\', '"'>(buf.position() + 1, buf.buffer().end()); @@ -77,10 +78,10 @@ StringRef JSONEachRowRowInputStream::readColumnName(ReadBuffer & buf) { /// The most likely option is that there is no escape sequence in the key name, and the entire name is placed in the buffer. assertChar('"', buf); - StringRef res(buf.position(), next_pos - buf.position()); + current_column_name.assign(buf.position(), next_pos - buf.position()); buf.position() += next_pos - buf.position(); assertChar('"', buf); - return res; + return current_column_name; } } @@ -150,7 +151,6 @@ void JSONEachRowRowInputStream::readJSONObject(MutableColumns & columns) for (size_t key_index = 0; advanceToNextKey(key_index); ++key_index) { StringRef name_ref = readColumnName(istr); - skipColonDelimeter(istr); const size_t column_index = columnIndex(name_ref); From 041cef5968ae98c77afca81ec7c4236d9f677dd3 Mon Sep 17 00:00:00 2001 From: chertus Date: Fri, 21 Sep 2018 18:20:23 +0300 Subject: [PATCH 11/78] explain ast [CLICKHOUSE-4001] --- dbms/src/Core/iostream_debug_helpers.cpp | 2 + .../Interpreters/InterpreterExplainQuery.cpp | 51 +++++++++++++++++++ .../Interpreters/InterpreterExplainQuery.h | 32 ++++++++++++ dbms/src/Interpreters/InterpreterFactory.cpp | 6 +++ dbms/src/Parsers/ASTExplainQuery.h | 45 ++++++++++++++++ dbms/src/Parsers/IAST.h | 21 ++++++++ dbms/src/Parsers/ParserQueryWithOutput.cpp | 15 +++++- 7 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 dbms/src/Interpreters/InterpreterExplainQuery.cpp create mode 100644 dbms/src/Interpreters/InterpreterExplainQuery.h create mode 100644 dbms/src/Parsers/ASTExplainQuery.h diff --git a/dbms/src/Core/iostream_debug_helpers.cpp b/dbms/src/Core/iostream_debug_helpers.cpp index 4500538b69e..6cbef6ac2d4 100644 --- a/dbms/src/Core/iostream_debug_helpers.cpp +++ b/dbms/src/Core/iostream_debug_helpers.cpp @@ -95,6 +95,7 @@ std::ostream & operator<<(std::ostream & stream, const Connection::Packet & what return stream; } +#if 0 std::ostream & operator<<(std::ostream & stream, const IAST & what) { stream << "IAST{"; @@ -102,5 +103,6 @@ std::ostream & operator<<(std::ostream & stream, const IAST & what) stream << "}"; return stream; } +#endif } diff --git a/dbms/src/Interpreters/InterpreterExplainQuery.cpp b/dbms/src/Interpreters/InterpreterExplainQuery.cpp new file mode 100644 index 00000000000..d7aac9c81a3 --- /dev/null +++ b/dbms/src/Interpreters/InterpreterExplainQuery.cpp @@ -0,0 +1,51 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +BlockIO InterpreterExplainQuery::execute() +{ + BlockIO res; + res.in = executeImpl(); + return res; +} + + +Block InterpreterExplainQuery::getSampleBlock() +{ + Block block; + + ColumnWithTypeAndName col; + col.name = "ast"; + col.type = std::make_shared(); + col.column = col.type->createColumn(); + block.insert(col); + + return block; +} + + +BlockInputStreamPtr InterpreterExplainQuery::executeImpl() +{ + const ASTExplainQuery & ast = typeid_cast(*query); + + std::stringstream ss; + ast.dumpSExpressions(ss); + + Block sample_block = getSampleBlock(); + MutableColumns res_columns = sample_block.cloneEmptyColumns(); + res_columns[0]->insert(ss.str()); + + return std::make_shared(sample_block.cloneWithColumns(std::move(res_columns))); +} + +} diff --git a/dbms/src/Interpreters/InterpreterExplainQuery.h b/dbms/src/Interpreters/InterpreterExplainQuery.h new file mode 100644 index 00000000000..828e10ecd0b --- /dev/null +++ b/dbms/src/Interpreters/InterpreterExplainQuery.h @@ -0,0 +1,32 @@ +#pragma once + +#include + + +namespace DB +{ + +class IAST; +using ASTPtr = std::shared_ptr; + + +/// Returns single row with explain results +class InterpreterExplainQuery : public IInterpreter +{ +public: + InterpreterExplainQuery(const ASTPtr & query_, const Context & ) + : query(query_) + {} + + BlockIO execute() override; + + static Block getSampleBlock(); + +private: + ASTPtr query; + + BlockInputStreamPtr executeImpl(); +}; + + +} diff --git a/dbms/src/Interpreters/InterpreterFactory.cpp b/dbms/src/Interpreters/InterpreterFactory.cpp index 673e80ceabf..5d1b259cc0d 100644 --- a/dbms/src/Interpreters/InterpreterFactory.cpp +++ b/dbms/src/Interpreters/InterpreterFactory.cpp @@ -12,12 +12,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -145,6 +147,10 @@ std::unique_ptr InterpreterFactory::get(ASTPtr & query, Context & { return std::make_unique(query, context); } + else if (typeid_cast(query.get())) + { + return std::make_unique(query, context); + } else if (typeid_cast(query.get())) { return std::make_unique(query, context); diff --git a/dbms/src/Parsers/ASTExplainQuery.h b/dbms/src/Parsers/ASTExplainQuery.h new file mode 100644 index 00000000000..b02731c79d3 --- /dev/null +++ b/dbms/src/Parsers/ASTExplainQuery.h @@ -0,0 +1,45 @@ +#pragma once + +#include + + +namespace DB +{ + + +/// AST, EXPLAIN or other query with meaning of explanation query instead of execution +class ASTExplainQuery : public IAST +{ +public: + enum ExplainKind + { + ParsedAST, + }; + + ASTExplainQuery(ExplainKind kind_ = ParsedAST) + : kind(kind_) + {} + + String getID() const override { return "Explain_" + toString(kind); } + ASTPtr clone() const override { return std::make_shared(*this); } + +protected: + void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << toString(kind) << (settings.hilite ? hilite_none : ""); + } + +private: + ExplainKind kind; + + static String toString(ExplainKind kind) + { + switch (kind) + { + case ParsedAST: return "ParsedAST"; + } + __builtin_unreachable(); + } +}; + +} diff --git a/dbms/src/Parsers/IAST.h b/dbms/src/Parsers/IAST.h index 5135e08a965..886109c4d61 100644 --- a/dbms/src/Parsers/IAST.h +++ b/dbms/src/Parsers/IAST.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -87,6 +88,26 @@ public: child->dumpTree(ostr, indent + 1); } + void dumpSExpressions(std::ostream & ostr, size_t indent = 0) const + { + String indent_str(indent, ' '); + String id = getID(); + std::replace(id.begin(), id.end(), '_', ' '); + ostr << indent_str << '(' << id; + id = tryGetAlias(); + if (!id.empty()) + ostr << " (Alias " << id << ')'; + if (!children.empty()) + { + ostr << std::endl; + for (const auto & child : children) + child->dumpSExpressions(ostr, indent + 1); + ostr << indent_str << ')' << std::endl; + } + else + ostr << ')' << std::endl; + } + /** Check the depth of the tree. * If max_depth is specified and the depth is greater - throw an exception. * Returns the depth of the tree. diff --git a/dbms/src/Parsers/ParserQueryWithOutput.cpp b/dbms/src/Parsers/ParserQueryWithOutput.cpp index 7c2b6afc442..5d7b75defd9 100644 --- a/dbms/src/Parsers/ParserQueryWithOutput.cpp +++ b/dbms/src/Parsers/ParserQueryWithOutput.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace DB @@ -33,6 +34,11 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec ASTPtr query; + ParserKeyword s_ast("AST"); + bool explain_ast = false; + if (s_ast.ignore(pos, expected)) + explain_ast = true; + bool parsed = select_p.parse(pos, query, expected) || show_tables_p.parse(pos, query, expected) || table_p.parse(pos, query, expected) @@ -74,7 +80,14 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec query_with_output.children.push_back(query_with_output.format); } - node = query; + if (explain_ast) + { + node = std::make_shared(); + node->children.push_back(query); + } + else + node = query; + return true; } From 4b431264db4f6e0595619ad3676d3a56536aed54 Mon Sep 17 00:00:00 2001 From: Alexandr Krasheninnikov Date: Thu, 27 Sep 2018 18:23:42 +0300 Subject: [PATCH 12/78] Implement quote getter from ODBC --- dbms/programs/odbc-bridge/HandlerFactory.cpp | 6 ++ dbms/programs/odbc-bridge/HandlerFactory.h | 3 +- .../odbc-bridge/IdentifierQuoteHandler.cpp | 80 +++++++++++++++++++ .../odbc-bridge/IdentifierQuoteHandler.h | 28 +++++++ dbms/src/Common/XDBCBridgeHelper.h | 70 +++++++--------- dbms/src/Storages/StorageXDBC.cpp | 4 +- dbms/src/TableFunctions/ITableFunctionXDBC.h | 2 +- 7 files changed, 148 insertions(+), 45 deletions(-) create mode 100644 dbms/programs/odbc-bridge/IdentifierQuoteHandler.cpp create mode 100644 dbms/programs/odbc-bridge/IdentifierQuoteHandler.h diff --git a/dbms/programs/odbc-bridge/HandlerFactory.cpp b/dbms/programs/odbc-bridge/HandlerFactory.cpp index 93579d921e8..8a0ff06268f 100644 --- a/dbms/programs/odbc-bridge/HandlerFactory.cpp +++ b/dbms/programs/odbc-bridge/HandlerFactory.cpp @@ -24,6 +24,12 @@ Poco::Net::HTTPRequestHandler * HandlerFactory::createRequestHandler(const Poco: return new ODBCColumnsInfoHandler(keep_alive_timeout, context); #else return nullptr; +#endif + else if(uri.getPath() == "/identifier_quote") +#if USE_POCO_SQLODBC || USE_POCO_DATAODBC + return new IdentifierQuoteHandler(keep_alive_timeout, context); +#else + return nullptr; #endif else return new ODBCHandler(pool_map, keep_alive_timeout, context); diff --git a/dbms/programs/odbc-bridge/HandlerFactory.h b/dbms/programs/odbc-bridge/HandlerFactory.h index 4fe00ffca98..f1a6803f65b 100644 --- a/dbms/programs/odbc-bridge/HandlerFactory.h +++ b/dbms/programs/odbc-bridge/HandlerFactory.h @@ -5,6 +5,7 @@ #include #include "MainHandler.h" #include "ColumnInfoHandler.h" +#include "IdentifierQuoteHandler.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" @@ -14,7 +15,7 @@ namespace DB { -/** Factory for '/ping', '/' and '/columns_info' handlers. +/** Factory for '/ping', '/', '/columns_info', '/identifier_quote' handlers. * Also stores Session pools for ODBC connections */ class HandlerFactory : public Poco::Net::HTTPRequestHandlerFactory diff --git a/dbms/programs/odbc-bridge/IdentifierQuoteHandler.cpp b/dbms/programs/odbc-bridge/IdentifierQuoteHandler.cpp new file mode 100644 index 00000000000..ff7d11c3208 --- /dev/null +++ b/dbms/programs/odbc-bridge/IdentifierQuoteHandler.cpp @@ -0,0 +1,80 @@ +#include "IdentifierQuoteHandler.h" +#if USE_POCO_SQLODBC || USE_POCO_DATAODBC + +#if USE_POCO_SQLODBC +#include +#include +#include +#define POCO_SQL_ODBC_CLASS Poco::SQL::ODBC +#endif +#if USE_POCO_DATAODBC +#include +#include +#include +#define POCO_SQL_ODBC_CLASS Poco::Data::ODBC +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "validateODBCConnectionString.h" + +namespace DB +{ + +void IdentifierQuoteHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) +{ + Poco::Net::HTMLForm params(request, request.stream()); + LOG_TRACE(log, "Request URI: " + request.getURI()); + + auto process_error = [&response, this](const std::string & message) + { + response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); + if (!response.sent()) + response.send() << message << std::endl; + LOG_WARNING(log, message); + }; + + if (!params.has("connection_string")) + { + process_error("No 'connection_string' in request URL"); + return; + } + + try + { + std::string connection_string = params.get("connection_string"); + POCO_SQL_ODBC_CLASS::SessionImpl session(validateODBCConnectionString(connection_string), DBMS_DEFAULT_CONNECT_TIMEOUT_SEC); + SQLHDBC hdbc = session.dbc().handle(); + + std::string identifier; + + SQLSMALLINT t; + SQLRETURN r = Poco::Data::ODBC::SQLGetInfo(hdbc, SQL_IDENTIFIER_QUOTE_CHAR, NULL, 0, &t); + if (!POCO_SQL_ODBC_CLASS::Utility::isError(r) && t > 0) + { + identifier.resize(static_cast(t) + 2); + r = Poco::Data::ODBC::SQLGetInfo(hdbc, SQL_IDENTIFIER_QUOTE_CHAR, &identifier[0], SQLSMALLINT((identifier.length() - 1) * sizeof(identifier[0])), &t); + LOG_TRACE(log, "GOT IDENTIFIER: '" + identifier + "'"); + } + + LOG_TRACE(log, "FINAL IDENTIFIER: '" + identifier + "'"); + + WriteBufferFromHTTPServerResponse out(request, response, keep_alive_timeout); + writeStringBinary(identifier, out); + } + catch (...) + { + process_error("Error getting identifier quote style from ODBC '" + getCurrentExceptionMessage(false) + "'"); + tryLogCurrentException(log); + } +} +} +#endif diff --git a/dbms/programs/odbc-bridge/IdentifierQuoteHandler.h b/dbms/programs/odbc-bridge/IdentifierQuoteHandler.h new file mode 100644 index 00000000000..e7fff8ac001 --- /dev/null +++ b/dbms/programs/odbc-bridge/IdentifierQuoteHandler.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include +#include + +#if USE_POCO_SQLODBC || USE_POCO_DATAODBC +/** This handler establish connection to database, and retrieve quote style identifier + */ +namespace DB +{ +class IdentifierQuoteHandler : public Poco::Net::HTTPRequestHandler +{ +public: + IdentifierQuoteHandler(size_t keep_alive_timeout_, std::shared_ptr context_) + : log(&Poco::Logger::get("IdentifierQuoteHandler")), keep_alive_timeout(keep_alive_timeout_), context(context_) + { + } + + void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) override; + +private: + Poco::Logger * log; + size_t keep_alive_timeout; + std::shared_ptr context; +}; +} +#endif diff --git a/dbms/src/Common/XDBCBridgeHelper.h b/dbms/src/Common/XDBCBridgeHelper.h index 3e34ad80189..a920594ab15 100644 --- a/dbms/src/Common/XDBCBridgeHelper.h +++ b/dbms/src/Common/XDBCBridgeHelper.h @@ -37,7 +37,7 @@ public: virtual void startBridgeSync() const = 0; virtual Poco::URI getMainURI() const = 0; virtual Poco::URI getColumnsInfoURI() const = 0; - virtual IdentifierQuotingStyle getQuotingStyle() const = 0; + virtual IdentifierQuotingStyle getIdentifierQuotingStyle() = 0; virtual ~IXDBCBridgeHelper() {} }; @@ -56,6 +56,8 @@ private: Poco::Logger * log = &Poco::Logger::get(BridgeHelperMixin::NAME); + std::optional quote_style; + protected: auto getConnectionString() const { @@ -73,6 +75,7 @@ public: static constexpr inline auto PING_HANDLER = "/ping"; static constexpr inline auto MAIN_HANDLER = "/"; static constexpr inline auto COL_INFO_HANDLER = "/columns_info"; + static constexpr inline auto IDENTIFIER_QUOTE_HANDLER = "/identifier_quote"; static constexpr inline auto PING_OK_ANSWER = "Ok."; XDBCBridgeHelper( @@ -90,9 +93,32 @@ public: virtual ~XDBCBridgeHelper() {} - IdentifierQuotingStyle getQuotingStyle() const override + IdentifierQuotingStyle getIdentifierQuotingStyle() override { - return BridgeHelperMixin::getQuotingStyle(); + std::cerr << "GETTING QUOTE STYLE " << std::endl; + if (!quote_style.has_value()) + { + auto uri = createBaseURI(); + uri.setPath(IDENTIFIER_QUOTE_HANDLER); + uri.addQueryParameter("connection_string", getConnectionString()); + + ReadWriteBufferFromHTTP buf(uri, Poco::Net::HTTPRequest::HTTP_POST, nullptr); + std::string character; + readStringBinary(character, buf); + if (character.length() > 1) + throw Exception("Failed to get quoting style from " + BridgeHelperMixin::serviceAlias()); + + if(character.length() == 0) + quote_style = IdentifierQuotingStyle::None; + else if(character[0] == '`') + quote_style = IdentifierQuotingStyle::Backticks; + else if(character[0] == '"') + quote_style = IdentifierQuotingStyle::DoubleQuotes; + else + throw Exception("Failed to determine quoting style from " + BridgeHelperMixin::serviceAlias() + " response: " + character); + } + + return *quote_style; } /** @@ -202,42 +228,6 @@ struct JDBCBridgeMixin throw Exception("jdbc-bridge does not support external auto-start"); } - static IdentifierQuotingStyle getQuotingStyle() { - throw Exception("This method should not be called directly"); - } -}; - -class JDBCBridgeHelper : public XDBCBridgeHelper -{ - static constexpr inline auto QUOTE_STYLE_HANDLER = "/quote_style"; - -public: - - JDBCBridgeHelper( - const Poco::Util::AbstractConfiguration & config_, - const Poco::Timespan & http_timeout_, - const std::string & connection_string_) - : XDBCBridgeHelper(config_, http_timeout_, connection_string_) {} - - IdentifierQuotingStyle getQuotingStyle() const override { - - auto uri = createBaseURI(); - uri.setPath(QUOTE_STYLE_HANDLER); - uri.addQueryParameter("connection_string", getConnectionString()); - - ReadWriteBufferFromHTTP buf(uri, Poco::Net::HTTPRequest::HTTP_GET, nullptr); - std::string character; - readStringBinary(character, buf); - if (character.length() > 1) - throw Exception("Failed to get quoting style from jdbc-bridge"); - - if(character[0] == '`') - return IdentifierQuotingStyle::Backticks; - else if(character[0] == '"') - return IdentifierQuotingStyle::DoubleQuotes; - else - throw Exception("Failed to determine quoting style from jdbc-bridge response: " + character); - } }; struct ODBCBridgeMixin { @@ -248,8 +238,6 @@ struct ODBCBridgeMixin { static const String configPrefix() { return "odbc_bridge"; } static const String serviceAlias() { return "clickhouse-odbc-bridge"; } - static IdentifierQuotingStyle getQuotingStyle() { return IdentifierQuotingStyle::DoubleQuotes; } - static void startBridge(const Poco::Util::AbstractConfiguration & config, const Poco::Logger * , const Poco::Timespan & http_timeout) { Poco::Path path{config.getString("application.dir", "")}; diff --git a/dbms/src/Storages/StorageXDBC.cpp b/dbms/src/Storages/StorageXDBC.cpp index ec72e9594db..d25c903fdd9 100644 --- a/dbms/src/Storages/StorageXDBC.cpp +++ b/dbms/src/Storages/StorageXDBC.cpp @@ -68,7 +68,7 @@ namespace DB size_t /*max_block_size*/) const { String query = transformQueryForExternalDatabase( - *query_info.query, getColumns().ordinary, bridge_helper->getQuotingStyle(), remote_database_name, remote_table_name, context); + *query_info.query, getColumns().ordinary, bridge_helper->getIdentifierQuotingStyle(), remote_database_name, remote_table_name, context); return [query](std::ostream & os) { os << "query=" << query; }; } @@ -105,7 +105,7 @@ namespace DB for (size_t i = 0; i < 3; ++i) engine_args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[i], args.local_context); - BridgeHelperPtr bridge_helper = std::make_shared( + BridgeHelperPtr bridge_helper = std::make_shared>( args.context.getConfigRef(), args.context.getSettingsRef().http_receive_timeout.value, static_cast(*engine_args[0]).value.safeGet() diff --git a/dbms/src/TableFunctions/ITableFunctionXDBC.h b/dbms/src/TableFunctions/ITableFunctionXDBC.h index 8b46453aeea..1fde772a187 100644 --- a/dbms/src/TableFunctions/ITableFunctionXDBC.h +++ b/dbms/src/TableFunctions/ITableFunctionXDBC.h @@ -52,7 +52,7 @@ private: BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_) const override { - return std::make_shared(config_, http_timeout_, connection_string_); + return std::make_shared>(config_, http_timeout_, connection_string_); } }; From 57d2c9db6094dbdc4490926572c76c6ad2096554 Mon Sep 17 00:00:00 2001 From: chertus Date: Thu, 27 Sep 2018 22:25:18 +0300 Subject: [PATCH 13/78] AST debug stuff: DumpASTNode, TranslateQualifiedNamesVisitor, hide AST explain command [CLICKHOUSE-4001] --- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 100 +------------- dbms/src/Interpreters/ExpressionAnalyzer.h | 2 - .../Interpreters/InterpreterExplainQuery.cpp | 2 +- .../PredicateExpressionsOptimizer.cpp | 3 +- .../TranslateQualifiedNamesVisitor.cpp | 123 ++++++++++++++++++ .../TranslateQualifiedNamesVisitor.h | 65 +++++++++ dbms/src/Interpreters/evaluateQualified.cpp | 4 + dbms/src/Interpreters/evaluateQualified.h | 12 +- dbms/src/Interpreters/getQueryAliases.cpp | 10 +- dbms/src/Interpreters/getQueryAliases.h | 22 +++- dbms/src/Parsers/IAST.h | 90 ++++++++++--- dbms/src/Parsers/ParserQueryWithOutput.cpp | 2 + 12 files changed, 311 insertions(+), 124 deletions(-) create mode 100644 dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp create mode 100644 dbms/src/Interpreters/TranslateQualifiedNamesVisitor.h diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 1924608b676..a8ed01efbe7 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -229,7 +230,8 @@ ExpressionAnalyzer::ExpressionAnalyzer( LogicalExpressionsOptimizer(select_query, settings).perform(); /// Creates a dictionary `aliases`: alias -> ASTPtr - getQueryAliases(query, aliases); + QueryAliasesVisitor queryAliasesVisitor; + queryAliasesVisitor.visit(query, aliases); /// Common subexpression elimination. Rewrite rules. normalizeTree(); @@ -312,97 +314,8 @@ void ExpressionAnalyzer::translateQualifiedNames() for (const auto & table_expression : tables_expression) tables.emplace_back(getTableNameWithAliasFromTableExpression(table_expression, context)); - translateQualifiedNamesImpl(query, tables); -} - -void ExpressionAnalyzer::translateQualifiedNamesImpl(ASTPtr & ast, const std::vector & tables) -{ - if (auto * identifier = typeid_cast(ast.get())) - { - if (identifier->general()) - { - /// Select first table name with max number of qualifiers which can be stripped. - size_t max_num_qualifiers_to_strip = 0; - size_t best_table_pos = 0; - - for (size_t table_pos = 0; table_pos < tables.size(); ++table_pos) - { - const auto & table = tables[table_pos]; - auto num_qualifiers_to_strip = getNumComponentsToStripInOrderToTranslateQualifiedName(*identifier, table); - - if (num_qualifiers_to_strip > max_num_qualifiers_to_strip) - { - max_num_qualifiers_to_strip = num_qualifiers_to_strip; - best_table_pos = table_pos; - } - } - - stripIdentifier(ast, max_num_qualifiers_to_strip); - - /// In case if column from the joined table are in source columns, change it's name to qualified. - if (best_table_pos && source_columns.contains(ast->getColumnName())) - tables[best_table_pos].makeQualifiedName(ast); - } - } - else if (typeid_cast(ast.get())) - { - if (ast->children.size() != 1) - throw Exception("Logical error: qualified asterisk must have exactly one child", ErrorCodes::LOGICAL_ERROR); - - ASTIdentifier * ident = typeid_cast(ast->children[0].get()); - if (!ident) - throw Exception("Logical error: qualified asterisk must have identifier as its child", ErrorCodes::LOGICAL_ERROR); - - size_t num_components = ident->children.size(); - if (num_components > 2) - throw Exception("Qualified asterisk cannot have more than two qualifiers", ErrorCodes::UNKNOWN_ELEMENT_IN_AST); - - for (const auto & table_names : tables) - { - /// database.table.*, table.* or alias.* - if ((num_components == 2 - && !table_names.database.empty() - && static_cast(*ident->children[0]).name == table_names.database - && static_cast(*ident->children[1]).name == table_names.table) - || (num_components == 0 - && ((!table_names.table.empty() && ident->name == table_names.table) - || (!table_names.alias.empty() && ident->name == table_names.alias)))) - { - return; - } - } - - throw Exception("Unknown qualified identifier: " + ident->getAliasOrColumnName(), ErrorCodes::UNKNOWN_IDENTIFIER); - } - else if (auto * join = typeid_cast(ast.get())) - { - /// Don't translate on_expression here in order to resolve equation parts later. - if (join->using_expression_list) - translateQualifiedNamesImpl(join->using_expression_list, tables); - } - else - { - /// If the WHERE clause or HAVING consists of a single quailified column, the reference must be translated not only in children, but also in where_expression and having_expression. - if (ASTSelectQuery * select = typeid_cast(ast.get())) - { - if (select->prewhere_expression) - translateQualifiedNamesImpl(select->prewhere_expression, tables); - if (select->where_expression) - translateQualifiedNamesImpl(select->where_expression, tables); - if (select->having_expression) - translateQualifiedNamesImpl(select->having_expression, tables); - } - - for (auto & child : ast->children) - { - /// Do not go to FROM, JOIN, subqueries. - if (!typeid_cast(child.get()) - && !typeid_cast(child.get())) - { - translateQualifiedNamesImpl(child, tables); - } - } - } + TranslateQualifiedNamesVisitor visitor(source_columns, tables); + visitor.visit(query); } void ExpressionAnalyzer::optimizeIfWithConstantCondition() @@ -2953,8 +2866,7 @@ void ExpressionAnalyzer::collectJoinedColumnsFromJoinOnExpr() std::function translate_qualified_names; translate_qualified_names = [&](ASTPtr & ast, const DatabaseAndTableWithAlias & source_names) { - auto * identifier = typeid_cast(ast.get()); - if (identifier) + if (auto * identifier = typeid_cast(ast.get())) { if (identifier->general()) { diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.h b/dbms/src/Interpreters/ExpressionAnalyzer.h index 4b2b0d818e0..a9e190aef7a 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.h +++ b/dbms/src/Interpreters/ExpressionAnalyzer.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -414,7 +413,6 @@ private: * only one ("main") table is supported. Ambiguity is not detected or resolved. */ void translateQualifiedNames(); - void translateQualifiedNamesImpl(ASTPtr & node, const std::vector & tables); /** Sometimes we have to calculate more columns in SELECT clause than will be returned from query. * This is the case when we have DISTINCT or arrayJoin: we require more columns in SELECT even if we need less columns in result. diff --git a/dbms/src/Interpreters/InterpreterExplainQuery.cpp b/dbms/src/Interpreters/InterpreterExplainQuery.cpp index d7aac9c81a3..a8dc77c07c7 100644 --- a/dbms/src/Interpreters/InterpreterExplainQuery.cpp +++ b/dbms/src/Interpreters/InterpreterExplainQuery.cpp @@ -39,7 +39,7 @@ BlockInputStreamPtr InterpreterExplainQuery::executeImpl() const ASTExplainQuery & ast = typeid_cast(*query); std::stringstream ss; - ast.dumpSExpressions(ss); + dumpAST(ast, ss); Block sample_block = getSampleBlock(); MutableColumns res_columns = sample_block.cloneEmptyColumns(); diff --git a/dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp b/dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp index 812dc3a0b7d..3bcb6ba841c 100644 --- a/dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp +++ b/dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp @@ -302,7 +302,8 @@ ASTs PredicateExpressionsOptimizer::getSelectQueryProjectionColumns(ASTPtr & ast { /// first should normalize query tree. std::unordered_map aliases; - getQueryAliases(ast, aliases, 0); + QueryAliasesVisitor queryAliasesVisitor; + queryAliasesVisitor.visit(ast, aliases, 0); QueryNormalizer(ast, aliases, settings, {}, {}).perform(); ASTs projection_columns; diff --git a/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp new file mode 100644 index 00000000000..adc8761372b --- /dev/null +++ b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp @@ -0,0 +1,123 @@ +#include + +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int UNKNOWN_IDENTIFIER; +} + +void TranslateQualifiedNamesVisitor::visit(ASTIdentifier * identifier, ASTPtr & ast) const +{ + DumpASTNode dump(*identifier, ostr, visit_depth, visitor_label); + + if (identifier->general()) + { + /// Select first table name with max number of qualifiers which can be stripped. + size_t max_num_qualifiers_to_strip = 0; + size_t best_table_pos = 0; + + for (size_t table_pos = 0; table_pos < tables.size(); ++table_pos) + { + const auto & table = tables[table_pos]; + auto num_qualifiers_to_strip = getNumComponentsToStripInOrderToTranslateQualifiedName(*identifier, table); + + if (num_qualifiers_to_strip > max_num_qualifiers_to_strip) + { + max_num_qualifiers_to_strip = num_qualifiers_to_strip; + best_table_pos = table_pos; + } + } + + stripIdentifier(ast, max_num_qualifiers_to_strip); + + /// In case if column from the joined table are in source columns, change it's name to qualified. + if (best_table_pos && source_columns.contains(ast->getColumnName())) + { + const DatabaseAndTableWithAlias & table = tables[best_table_pos]; + table.makeQualifiedName(ast); + dump.print("makeQualifiedName", table.database + '.' + table.table + ' ' + ast->getColumnName()); + } + } +} + +void TranslateQualifiedNamesVisitor::visit(ASTQualifiedAsterisk *, ASTPtr & ast) const +{ + DumpASTNode dump(*ast, ostr, visit_depth, visitor_label); + + if (ast->children.size() != 1) + throw Exception("Logical error: qualified asterisk must have exactly one child", ErrorCodes::LOGICAL_ERROR); + + ASTIdentifier * ident = typeid_cast(ast->children[0].get()); + if (!ident) + throw Exception("Logical error: qualified asterisk must have identifier as its child", ErrorCodes::LOGICAL_ERROR); + + size_t num_components = ident->children.size(); + if (num_components > 2) + throw Exception("Qualified asterisk cannot have more than two qualifiers", ErrorCodes::UNKNOWN_ELEMENT_IN_AST); + + for (const auto & table_names : tables) + { + /// database.table.*, table.* or alias.* + if ((num_components == 2 + && !table_names.database.empty() + && static_cast(*ident->children[0]).name == table_names.database + && static_cast(*ident->children[1]).name == table_names.table) + || (num_components == 0 + && ((!table_names.table.empty() && ident->name == table_names.table) + || (!table_names.alias.empty() && ident->name == table_names.alias)))) + { + return; + } + } + + throw Exception("Unknown qualified identifier: " + ident->getAliasOrColumnName(), ErrorCodes::UNKNOWN_IDENTIFIER); +} + +void TranslateQualifiedNamesVisitor::visit(ASTTableJoin * join, ASTPtr &) const +{ + DumpASTNode dump(*join, ostr, visit_depth, visitor_label); + + /// Don't translate on_expression here in order to resolve equation parts later. + if (join->using_expression_list) + visit(join->using_expression_list); +} + +void TranslateQualifiedNamesVisitor::visit(ASTSelectQuery * select, ASTPtr & ast) const +{ + DumpASTNode dump(*select, ostr, visit_depth, visitor_label); + + /// If the WHERE clause or HAVING consists of a single quailified column, the reference must be translated not only in children, + /// but also in where_expression and having_expression. + if (select->prewhere_expression) + visit(select->prewhere_expression); + if (select->where_expression) + visit(select->where_expression); + if (select->having_expression) + visit(select->having_expression); + + visitChildren(ast); +} + +void TranslateQualifiedNamesVisitor::visitChildren(ASTPtr & ast) const +{ + for (auto & child : ast->children) + { + /// Do not go to FROM, JOIN, subqueries. + if (!typeid_cast(child.get()) + && !typeid_cast(child.get())) + { + visit(child); + } + } +} + +} diff --git a/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.h b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.h new file mode 100644 index 00000000000..88246f71a83 --- /dev/null +++ b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.h @@ -0,0 +1,65 @@ +#include +#include + +#include +#include + +namespace DB +{ + +class ASTIdentifier; +class ASTQualifiedAsterisk; +class ASTSelectQuery; +struct ASTTableJoin; + +class NamesAndTypesList; + + +class TranslateQualifiedNamesVisitor +{ +public: + static constexpr const char * visitor_label = "translateQualifiedNames"; + + TranslateQualifiedNamesVisitor(const NamesAndTypesList & source_columns_, const std::vector & tables_, + std::ostream * ostr_ = nullptr) + : source_columns(source_columns_), + tables(tables_), + visit_depth(0), + ostr(ostr_) + {} + + void visit(ASTPtr & ast) const + { + if (!tryVisit(ast) && + !tryVisit(ast) && + !tryVisit(ast) && + !tryVisit(ast)) + visitChildren(ast); /// default: do nothing, visit children + } + +private: + const NamesAndTypesList & source_columns; + const std::vector & tables; + mutable size_t visit_depth; + std::ostream * ostr; + + void visit(ASTIdentifier * node, ASTPtr & ast) const; + void visit(ASTQualifiedAsterisk * node, ASTPtr & ast) const; + void visit(ASTTableJoin * node, ASTPtr & ast) const; + void visit(ASTSelectQuery * ast, ASTPtr &) const; + + void visitChildren(ASTPtr &) const; + + template + bool tryVisit(ASTPtr & ast) const + { + if (T * t = typeid_cast(ast.get())) + { + visit(t, ast); + return true; + } + return false; + } +}; + +} diff --git a/dbms/src/Interpreters/evaluateQualified.cpp b/dbms/src/Interpreters/evaluateQualified.cpp index 0c7604560a9..068e1896130 100644 --- a/dbms/src/Interpreters/evaluateQualified.cpp +++ b/dbms/src/Interpreters/evaluateQualified.cpp @@ -2,6 +2,10 @@ #include #include +#include +#include +#include + namespace DB { diff --git a/dbms/src/Interpreters/evaluateQualified.h b/dbms/src/Interpreters/evaluateQualified.h index 01f1dc8f9a6..32d338f8de6 100644 --- a/dbms/src/Interpreters/evaluateQualified.h +++ b/dbms/src/Interpreters/evaluateQualified.h @@ -1,13 +1,17 @@ #pragma once -#include -#include -#include #include namespace DB { +class IAST; +using ASTPtr = std::shared_ptr; + +class ASTIdentifier; +struct ASTTableExpression; + + struct DatabaseAndTableWithAlias { String database; @@ -31,4 +35,4 @@ size_t getNumComponentsToStripInOrderToTranslateQualifiedName(const ASTIdentifie std::pair getDatabaseAndTableNameFromIdentifier(const ASTIdentifier & identifier); -} \ No newline at end of file +} diff --git a/dbms/src/Interpreters/getQueryAliases.cpp b/dbms/src/Interpreters/getQueryAliases.cpp index d1096b8100b..c3387dbd018 100644 --- a/dbms/src/Interpreters/getQueryAliases.cpp +++ b/dbms/src/Interpreters/getQueryAliases.cpp @@ -18,8 +18,9 @@ namespace ErrorCodes /// ignore_levels - aliases in how many upper levels of the subtree should be ignored. /// For example, with ignore_levels=1 ast can not be put in the dictionary, but its children can. -void getQueryAliases(ASTPtr & ast, Aliases & aliases, int ignore_levels) +void QueryAliasesVisitor::getQueryAliases(const ASTPtr & ast, Aliases & aliases, int ignore_levels) const { + DumpASTNode dump(*ast, ostr, visit_depth, "getQueryAliases"); /// Bottom-up traversal. We do not go into subqueries. for (auto & child : ast->children) @@ -40,6 +41,11 @@ void getQueryAliases(ASTPtr & ast, Aliases & aliases, int ignore_levels) if (ignore_levels > 0) return; + getNodeAlias(ast, aliases, dump); +} + +void QueryAliasesVisitor::getNodeAlias(const ASTPtr & ast, Aliases & aliases, const DumpASTNode & dump) const +{ String alias = ast->tryGetAlias(); if (!alias.empty()) { @@ -56,6 +62,7 @@ void getQueryAliases(ASTPtr & ast, Aliases & aliases, int ignore_levels) } aliases[alias] = ast; + dump.print(visit_action, alias); } else if (auto subquery = typeid_cast(ast.get())) { @@ -76,6 +83,7 @@ void getQueryAliases(ASTPtr & ast, Aliases & aliases, int ignore_levels) subquery->setAlias(alias); subquery->prefer_alias_to_column_name = true; aliases[alias] = ast; + dump.print(visit_action, alias); } } } diff --git a/dbms/src/Interpreters/getQueryAliases.h b/dbms/src/Interpreters/getQueryAliases.h index fcd18c84dbd..d304336d043 100644 --- a/dbms/src/Interpreters/getQueryAliases.h +++ b/dbms/src/Interpreters/getQueryAliases.h @@ -8,6 +8,26 @@ namespace DB using Aliases = std::unordered_map; -void getQueryAliases(ASTPtr & ast, Aliases & aliases, int ignore_levels = 0); +class QueryAliasesVisitor +{ +public: + QueryAliasesVisitor(std::ostream * ostr_ = nullptr) + : visit_depth(0), + ostr(ostr_) + {} + + void visit(const ASTPtr & ast, Aliases & aliases, int ignore_levels = 0) const + { + getQueryAliases(ast, aliases, ignore_levels); + } + +private: + static constexpr const char * visit_action = "addAlias"; + mutable size_t visit_depth; + std::ostream * ostr; + + void getQueryAliases(const ASTPtr & ast, Aliases & aliases, int ignore_levels) const; + void getNodeAlias(const ASTPtr & ast, Aliases & aliases, const DumpASTNode & dump) const; +}; } diff --git a/dbms/src/Parsers/IAST.h b/dbms/src/Parsers/IAST.h index 886109c4d61..6da6e5503f7 100644 --- a/dbms/src/Parsers/IAST.h +++ b/dbms/src/Parsers/IAST.h @@ -88,26 +88,6 @@ public: child->dumpTree(ostr, indent + 1); } - void dumpSExpressions(std::ostream & ostr, size_t indent = 0) const - { - String indent_str(indent, ' '); - String id = getID(); - std::replace(id.begin(), id.end(), '_', ' '); - ostr << indent_str << '(' << id; - id = tryGetAlias(); - if (!id.empty()) - ostr << " (Alias " << id << ')'; - if (!children.empty()) - { - ostr << std::endl; - for (const auto & child : children) - child->dumpSExpressions(ostr, indent + 1); - ostr << indent_str << ')' << std::endl; - } - else - ostr << ')' << std::endl; - } - /** Check the depth of the tree. * If max_depth is specified and the depth is greater - throw an exception. * Returns the depth of the tree. @@ -240,4 +220,74 @@ private: String backQuoteIfNeed(const String & x); +/// If output stream set dumps node with indents and some additional info. Do nothing otherwise. +/// Allow to print kay-value pairs inside of tree dump. +class DumpASTNode +{ +public: + DumpASTNode(const IAST & ast_, std::ostream * ostr_, size_t & depth, const char * label = nullptr) + : ast(ast_), + ostr(ostr_), + indent(depth), + visit_depth(depth) + { + if (!ostr) + return; + if (visit_depth == 0) + (*ostr) << "-- " << (label ? label : "ast") << std::endl; + ++visit_depth; + + String id = ast.getID(); + std::replace(id.begin(), id.end(), '_', ' '); + (*ostr) << String(indent, ' ') << id; + + id = ast.tryGetAlias(); + if (!id.empty()) + print("alias", id, " "); + + if (!ast.children.empty()) + print("/", ast.children.size(), " "); /// slash is just a short name for 'children' here + + (*ostr) << std::endl; + } + + ~DumpASTNode() + { + if (!ostr) + return; + --visit_depth; + if (visit_depth == 0) + (*ostr) << "--" << std::endl; + } + + template + void print(const T & name, const U & value, const char * str_indent = nullptr) const + { + if (!ostr) + return; + + (*ostr) << (str_indent ? String(str_indent) : String(indent, ' ')); + (*ostr) << '(' << name << ' ' << value << ')'; + if (!str_indent) + (*ostr) << std::endl; + } + + size_t & getDepth() { return visit_depth; } + +private: + const IAST & ast; + std::ostream * ostr; + size_t indent; + size_t & visit_depth; /// shared with children +}; + +inline void dumpAST(const IAST & ast, std::ostream & ostr, DumpASTNode * parent = nullptr) +{ + size_t depth = 0; + DumpASTNode dump(ast, &ostr, (parent ? parent->getDepth() : depth), __FUNCTION__); + + for (const auto & child : ast.children) + dumpAST(*child, ostr, &dump); +} + } diff --git a/dbms/src/Parsers/ParserQueryWithOutput.cpp b/dbms/src/Parsers/ParserQueryWithOutput.cpp index 5d7b75defd9..c1952ecaf28 100644 --- a/dbms/src/Parsers/ParserQueryWithOutput.cpp +++ b/dbms/src/Parsers/ParserQueryWithOutput.cpp @@ -36,8 +36,10 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec ParserKeyword s_ast("AST"); bool explain_ast = false; +#if 0 if (s_ast.ignore(pos, expected)) explain_ast = true; +#endif bool parsed = select_p.parse(pos, query, expected) || show_tables_p.parse(pos, query, expected) From ceea4b9b33637465b9d3d46b669e1de924b3d95a Mon Sep 17 00:00:00 2001 From: Alexander Krasheninnikov Date: Fri, 28 Sep 2018 05:46:33 +0300 Subject: [PATCH 14/78] New bridge interaction, quote identifier via api --- dbms/programs/odbc-bridge/CMakeLists.txt | 2 + .../odbc-bridge/ColumnInfoHandler.cpp | 15 +- .../odbc-bridge/IdentifierQuoteHandler.cpp | 16 +- .../odbc-bridge/getIdentifierQuote.cpp | 45 ++++++ .../programs/odbc-bridge/getIdentifierQuote.h | 16 ++ dbms/src/Common/ODBCBridgeHelper.cpp | 151 ------------------ dbms/src/Common/ODBCBridgeHelper.h | 52 ------ dbms/src/Common/XDBCBridgeHelper.h | 34 ++-- .../Dictionaries/DictionarySourceFactory.cpp | 13 +- dbms/src/Dictionaries/ODBCDictionarySource.h | 90 ----------- ...arySource.cpp => XDBCDictionarySource.cpp} | 80 +++++----- dbms/src/Dictionaries/XDBCDictionarySource.h | 88 ++++++++++ dbms/src/Dictionaries/readInvalidateQuery.h | 2 +- dbms/src/Storages/StorageODBC.cpp | 115 ------------- dbms/src/Storages/StorageODBC.h | 61 ------- dbms/src/Storages/StorageXDBC.cpp | 91 +++++------ dbms/src/Storages/StorageXDBC.h | 34 +--- dbms/src/Storages/registerStorages.cpp | 2 - .../src/TableFunctions/ITableFunctionXDBC.cpp | 10 +- dbms/src/TableFunctions/ITableFunctionXDBC.h | 28 +--- dbms/src/TableFunctions/TableFunctionODBC.cpp | 86 ---------- dbms/src/TableFunctions/TableFunctionODBC.h | 22 --- .../TableFunctions/registerTableFunctions.cpp | 2 - 23 files changed, 288 insertions(+), 767 deletions(-) create mode 100644 dbms/programs/odbc-bridge/getIdentifierQuote.cpp create mode 100644 dbms/programs/odbc-bridge/getIdentifierQuote.h delete mode 100644 dbms/src/Common/ODBCBridgeHelper.cpp delete mode 100644 dbms/src/Common/ODBCBridgeHelper.h delete mode 100644 dbms/src/Dictionaries/ODBCDictionarySource.h rename dbms/src/Dictionaries/{ODBCDictionarySource.cpp => XDBCDictionarySource.cpp} (65%) create mode 100644 dbms/src/Dictionaries/XDBCDictionarySource.h delete mode 100644 dbms/src/Storages/StorageODBC.cpp delete mode 100644 dbms/src/Storages/StorageODBC.h delete mode 100644 dbms/src/TableFunctions/TableFunctionODBC.cpp delete mode 100644 dbms/src/TableFunctions/TableFunctionODBC.h diff --git a/dbms/programs/odbc-bridge/CMakeLists.txt b/dbms/programs/odbc-bridge/CMakeLists.txt index be0ff8d68bd..d13a2866e77 100644 --- a/dbms/programs/odbc-bridge/CMakeLists.txt +++ b/dbms/programs/odbc-bridge/CMakeLists.txt @@ -2,8 +2,10 @@ add_library (clickhouse-odbc-bridge-lib ${LINK_MODE} PingHandler.cpp MainHandler.cpp ColumnInfoHandler.cpp + IdentifierQuoteHandler.cpp HandlerFactory.cpp ODBCBridge.cpp + getIdentifierQuote.cpp validateODBCConnectionString.cpp ) diff --git a/dbms/programs/odbc-bridge/ColumnInfoHandler.cpp b/dbms/programs/odbc-bridge/ColumnInfoHandler.cpp index 014d09d9067..d5c7f7e0105 100644 --- a/dbms/programs/odbc-bridge/ColumnInfoHandler.cpp +++ b/dbms/programs/odbc-bridge/ColumnInfoHandler.cpp @@ -1,4 +1,5 @@ #include "ColumnInfoHandler.h" +#include "getIdentifierQuote.h" #if USE_POCO_SQLODBC || USE_POCO_DATAODBC #if USE_POCO_SQLODBC @@ -113,10 +114,22 @@ void ODBCColumnsInfoHandler::handleRequest(Poco::Net::HTTPServerRequest & reques IAST::FormatSettings settings(ss, true); settings.always_quote_identifiers = true; - settings.identifier_quoting_style = IdentifierQuotingStyle::DoubleQuotes; + + auto identifier_quote = getIdentifierQuote(hdbc); + if (identifier_quote.length() == 0) + settings.identifier_quoting_style = IdentifierQuotingStyle::None; + else if(identifier_quote[0] == '`') + settings.identifier_quoting_style = IdentifierQuotingStyle::Backticks; + else if(identifier_quote[0] == '"') + settings.identifier_quoting_style = IdentifierQuotingStyle::DoubleQuotes; + else + throw Exception("Can not map quote identifier '" + identifier_quote + "' to IdentifierQuotingStyle value"); + select->format(settings); std::string query = ss.str(); + std::cout << query << std::endl; + if (POCO_SQL_ODBC_CLASS::Utility::isError(POCO_SQL_ODBC_CLASS::SQLPrepare(hstmt, reinterpret_cast(query.data()), query.size()))) throw POCO_SQL_ODBC_CLASS::DescriptorException(session.dbc()); diff --git a/dbms/programs/odbc-bridge/IdentifierQuoteHandler.cpp b/dbms/programs/odbc-bridge/IdentifierQuoteHandler.cpp index ff7d11c3208..3588ff6c2ca 100644 --- a/dbms/programs/odbc-bridge/IdentifierQuoteHandler.cpp +++ b/dbms/programs/odbc-bridge/IdentifierQuoteHandler.cpp @@ -25,6 +25,7 @@ #include #include #include "validateODBCConnectionString.h" +#include "getIdentifierQuote.h" namespace DB { @@ -42,7 +43,7 @@ void IdentifierQuoteHandler::handleRequest(Poco::Net::HTTPServerRequest & reques LOG_WARNING(log, message); }; - if (!params.has("connection_string")) + if (!params.has("connection_string")) { process_error("No 'connection_string' in request URL"); return; @@ -54,18 +55,7 @@ void IdentifierQuoteHandler::handleRequest(Poco::Net::HTTPServerRequest & reques POCO_SQL_ODBC_CLASS::SessionImpl session(validateODBCConnectionString(connection_string), DBMS_DEFAULT_CONNECT_TIMEOUT_SEC); SQLHDBC hdbc = session.dbc().handle(); - std::string identifier; - - SQLSMALLINT t; - SQLRETURN r = Poco::Data::ODBC::SQLGetInfo(hdbc, SQL_IDENTIFIER_QUOTE_CHAR, NULL, 0, &t); - if (!POCO_SQL_ODBC_CLASS::Utility::isError(r) && t > 0) - { - identifier.resize(static_cast(t) + 2); - r = Poco::Data::ODBC::SQLGetInfo(hdbc, SQL_IDENTIFIER_QUOTE_CHAR, &identifier[0], SQLSMALLINT((identifier.length() - 1) * sizeof(identifier[0])), &t); - LOG_TRACE(log, "GOT IDENTIFIER: '" + identifier + "'"); - } - - LOG_TRACE(log, "FINAL IDENTIFIER: '" + identifier + "'"); + auto identifier = getIdentifierQuote(hdbc); WriteBufferFromHTTPServerResponse out(request, response, keep_alive_timeout); writeStringBinary(identifier, out); diff --git a/dbms/programs/odbc-bridge/getIdentifierQuote.cpp b/dbms/programs/odbc-bridge/getIdentifierQuote.cpp new file mode 100644 index 00000000000..5cfb5abba4b --- /dev/null +++ b/dbms/programs/odbc-bridge/getIdentifierQuote.cpp @@ -0,0 +1,45 @@ +#include "getIdentifierQuote.h" +#if USE_POCO_SQLODBC || USE_POCO_DATAODBC + +#if USE_POCO_SQLODBC +#include +#include +#include +#define POCO_SQL_ODBC_CLASS Poco::SQL::ODBC +#endif +#if USE_POCO_DATAODBC +#include +#include +#include +#define POCO_SQL_ODBC_CLASS Poco::Data::ODBC +#endif + + +namespace DB +{ + std::string getIdentifierQuote(SQLHDBC hdbc) + { + std::string identifier = ""; + + SQLSMALLINT t; + SQLRETURN r = POCO_SQL_ODBC_CLASS::SQLGetInfo(hdbc, SQL_IDENTIFIER_QUOTE_CHAR, NULL, 0, &t); + + if (POCO_SQL_ODBC_CLASS::Utility::isError(r)) + throw POCO_SQL_ODBC_CLASS::ConnectionException(hdbc); + + if (t > 0) + { + // I have not idea, why to add '2' here, got from: contrib/poco/Data/ODBC/src/ODBCStatementImpl.cpp:60 (SQL_DRIVER_NAME) + identifier.resize(static_cast(t) + 2); + + if (POCO_SQL_ODBC_CLASS::Utility::isError(POCO_SQL_ODBC_CLASS::SQLGetInfo(hdbc, SQL_IDENTIFIER_QUOTE_CHAR, &identifier[0], SQLSMALLINT((identifier.length() - 1) * sizeof(identifier[0])), &t))) + throw POCO_SQL_ODBC_CLASS::ConnectionException(hdbc); + + identifier.resize(static_cast(t)); + } + return identifier; + } + + +} +#endif diff --git a/dbms/programs/odbc-bridge/getIdentifierQuote.h b/dbms/programs/odbc-bridge/getIdentifierQuote.h new file mode 100644 index 00000000000..362e11b4e04 --- /dev/null +++ b/dbms/programs/odbc-bridge/getIdentifierQuote.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include +#include + +#if USE_POCO_SQLODBC || USE_POCO_DATAODBC +namespace DB +{ + + std::string getIdentifierQuote(SQLHDBC hdbc); + +} +#endif \ No newline at end of file diff --git a/dbms/src/Common/ODBCBridgeHelper.cpp b/dbms/src/Common/ODBCBridgeHelper.cpp deleted file mode 100644 index 2c9a502b1c8..00000000000 --- a/dbms/src/Common/ODBCBridgeHelper.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING; -} - -ODBCBridgeHelper::ODBCBridgeHelper( - const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_) - : config(config_), http_timeout(http_timeout_), connection_string(connection_string_) -{ - size_t bridge_port = config.getUInt("odbc_bridge.port", DEFAULT_PORT); - std::string bridge_host = config.getString("odbc_bridge.host", DEFAULT_HOST); - - ping_url.setHost(bridge_host); - ping_url.setPort(bridge_port); - ping_url.setScheme("http"); - ping_url.setPath(PING_HANDLER); -} - -void ODBCBridgeHelper::startODBCBridge() const -{ - Poco::Path path{config.getString("application.dir", "")}; - - path.setFileName( -#if CLICKHOUSE_SPLIT_BINARY - "clickhouse-odbc-bridge" -#else - "clickhouse" -#endif - ); - - if (!Poco::File(path).exists()) - throw Exception("clickhouse binary (" + path.toString() + ") is not found", ErrorCodes::EXTERNAL_EXECUTABLE_NOT_FOUND); - - std::stringstream command; - - command << path.toString() << -#if CLICKHOUSE_SPLIT_BINARY - " " -#else - " odbc-bridge " -#endif - ; - - command << "--http-port " << config.getUInt("odbc_bridge.port", DEFAULT_PORT) << ' '; - command << "--listen-host " << config.getString("odbc_bridge.listen_host", DEFAULT_HOST) << ' '; - command << "--http-timeout " << http_timeout.totalMicroseconds() << ' '; - if (config.has("logger.odbc_bridge_log")) - command << "--log-path " << config.getString("logger.odbc_bridge_log") << ' '; - if (config.has("logger.odbc_bridge_errlog")) - command << "--err-log-path " << config.getString("logger.odbc_bridge_errlog") << ' '; - if (config.has("logger.odbc_bridge_level")) - command << "--log-level " << config.getString("logger.odbc_bridge_level") << ' '; - command << "&"; /// we don't want to wait this process - - auto command_str = command.str(); - LOG_TRACE(log, "Starting clickhouse-odbc-bridge with command: " << command_str); - - auto cmd = ShellCommand::execute(command_str); - cmd->wait(); -} - -std::vector> ODBCBridgeHelper::getURLParams(const std::string & cols, size_t max_block_size) const -{ - std::vector> result; - - result.emplace_back("connection_string", connection_string); /// already validated - result.emplace_back("columns", cols); - result.emplace_back("max_block_size", std::to_string(max_block_size)); - - return result; -} - -bool ODBCBridgeHelper::checkODBCBridgeIsRunning() const -{ - try - { - ReadWriteBufferFromHTTP buf(ping_url, Poco::Net::HTTPRequest::HTTP_GET, nullptr); - return checkString(ODBCBridgeHelper::PING_OK_ANSWER, buf); - } - catch (...) - { - return false; - } -} - -void ODBCBridgeHelper::startODBCBridgeSync() const -{ - if (!checkODBCBridgeIsRunning()) - { - LOG_TRACE(log, "clickhouse-odbc-bridge is not running, will try to start it"); - startODBCBridge(); - bool started = false; - for (size_t counter : ext::range(1, 20)) - { - LOG_TRACE(log, "Checking clickhouse-odbc-bridge is running, try " << counter); - if (checkODBCBridgeIsRunning()) - { - started = true; - break; - } - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - if (!started) - throw Exception("ODBCBridgeHelper: clickhouse-odbc-bridge is not responding", ErrorCodes::EXTERNAL_SERVER_IS_NOT_RESPONDING); - } -} - -Poco::URI ODBCBridgeHelper::getMainURI() const -{ - size_t bridge_port = config.getUInt("odbc_bridge.port", DEFAULT_PORT); - std::string bridge_host = config.getString("odbc_bridge.host", DEFAULT_HOST); - - Poco::URI main_uri; - main_uri.setHost(bridge_host); - main_uri.setPort(bridge_port); - main_uri.setScheme("http"); - main_uri.setPath(MAIN_HANDLER); - return main_uri; -} - -Poco::URI ODBCBridgeHelper::getColumnsInfoURI() const -{ - size_t bridge_port = config.getUInt("odbc_bridge.port", DEFAULT_PORT); - std::string bridge_host = config.getString("odbc_bridge.host", DEFAULT_HOST); - - Poco::URI columns_info_uri; - columns_info_uri.setHost(bridge_host); - columns_info_uri.setPort(bridge_port); - columns_info_uri.setScheme("http"); - columns_info_uri.setPath(COL_INFO_HANDLER); - return columns_info_uri; -} -} diff --git a/dbms/src/Common/ODBCBridgeHelper.h b/dbms/src/Common/ODBCBridgeHelper.h deleted file mode 100644 index 807782d73eb..00000000000 --- a/dbms/src/Common/ODBCBridgeHelper.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace DB -{ -namespace ErrorCodes -{ - extern const int EXTERNAL_EXECUTABLE_NOT_FOUND; -} -/** Helper for odbc-bridge, provide utility methods, not main request - */ -class ODBCBridgeHelper -{ -private: - - using Configuration = Poco::Util::AbstractConfiguration; - - const Configuration & config; - Poco::Timespan http_timeout; - - std::string connection_string; - - Poco::URI ping_url; - - Poco::Logger * log = &Poco::Logger::get("ODBCBridgeHelper"); - -public: - static constexpr inline size_t DEFAULT_PORT = 9018; - - static constexpr inline auto DEFAULT_HOST = "localhost"; - static constexpr inline auto DEFAULT_FORMAT = "RowBinary"; - static constexpr inline auto PING_HANDLER = "/ping"; - static constexpr inline auto MAIN_HANDLER = "/"; - static constexpr inline auto COL_INFO_HANDLER = "/columns_info"; - static constexpr inline auto PING_OK_ANSWER = "Ok."; - - ODBCBridgeHelper(const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_); - - std::vector> getURLParams(const std::string & cols, size_t max_block_size) const; - bool checkODBCBridgeIsRunning() const; - - void startODBCBridge() const; - void startODBCBridgeSync() const; - - Poco::URI getMainURI() const; - Poco::URI getColumnsInfoURI() const; -}; -} diff --git a/dbms/src/Common/XDBCBridgeHelper.h b/dbms/src/Common/XDBCBridgeHelper.h index a920594ab15..af9033f6d7d 100644 --- a/dbms/src/Common/XDBCBridgeHelper.h +++ b/dbms/src/Common/XDBCBridgeHelper.h @@ -38,6 +38,7 @@ public: virtual Poco::URI getMainURI() const = 0; virtual Poco::URI getColumnsInfoURI() const = 0; virtual IdentifierQuotingStyle getIdentifierQuotingStyle() = 0; + virtual String getName() const = 0; virtual ~IXDBCBridgeHelper() {} }; @@ -54,7 +55,7 @@ private: Poco::URI ping_url; - Poco::Logger * log = &Poco::Logger::get(BridgeHelperMixin::NAME); + Poco::Logger * log = &Poco::Logger::get(BridgeHelperMixin::getName() + "BridgeHelper"); std::optional quote_style; @@ -93,9 +94,13 @@ public: virtual ~XDBCBridgeHelper() {} + String getName() const override + { + return BridgeHelperMixin::getName(); + } + IdentifierQuotingStyle getIdentifierQuotingStyle() override { - std::cerr << "GETTING QUOTE STYLE " << std::endl; if (!quote_style.has_value()) { auto uri = createBaseURI(); @@ -108,14 +113,14 @@ public: if (character.length() > 1) throw Exception("Failed to get quoting style from " + BridgeHelperMixin::serviceAlias()); - if(character.length() == 0) + if (character.length() == 0) quote_style = IdentifierQuotingStyle::None; else if(character[0] == '`') quote_style = IdentifierQuotingStyle::Backticks; else if(character[0] == '"') quote_style = IdentifierQuotingStyle::DoubleQuotes; else - throw Exception("Failed to determine quoting style from " + BridgeHelperMixin::serviceAlias() + " response: " + character); + throw Exception("Can not map quote identifier '" + character + "' to enum value"); } return *quote_style; @@ -156,7 +161,7 @@ public: std::this_thread::sleep_for(std::chrono::milliseconds(10)); } if (!started) - throw Exception("XDBCBridgeHelper: " + BridgeHelperMixin::serviceAlias() + " is not responding", ErrorCodes::EXTERNAL_SERVER_IS_NOT_RESPONDING); + throw Exception(BridgeHelperMixin::getName() + "BridgeHelper: " + BridgeHelperMixin::serviceAlias() + " is not responding", ErrorCodes::EXTERNAL_SERVER_IS_NOT_RESPONDING); } } @@ -219,13 +224,13 @@ private: struct JDBCBridgeMixin { static constexpr inline auto DEFAULT_PORT = 9019; - static constexpr inline auto NAME = "JDBCBridgeHelper"; static const String configPrefix() { return "jdbc_bridge"; } static const String serviceAlias() { return "clickhouse-jdbc-bridge"; } + static const String getName() { return "JDBC"; } static void startBridge(const Poco::Util::AbstractConfiguration & , const Poco::Logger * , const Poco::Timespan & ) { - throw Exception("jdbc-bridge does not support external auto-start"); + throw Exception("jdbc-bridge is not running. Please, start it manually"); } }; @@ -233,12 +238,12 @@ struct JDBCBridgeMixin struct ODBCBridgeMixin { static constexpr inline auto DEFAULT_PORT = 9018; - static constexpr inline auto NAME = "ODBCBridgeHelper"; static const String configPrefix() { return "odbc_bridge"; } static const String serviceAlias() { return "clickhouse-odbc-bridge"; } + static const String getName() { return "ODBC"; } - static void startBridge(const Poco::Util::AbstractConfiguration & config, const Poco::Logger * , const Poco::Timespan & http_timeout) + static void startBridge(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log, const Poco::Timespan & http_timeout) { Poco::Path path{config.getString("application.dir", "")}; @@ -266,16 +271,19 @@ struct ODBCBridgeMixin { command << "--http-port " << config.getUInt(configPrefix() + ".port", DEFAULT_PORT) << ' '; command << "--listen-host " << config.getString(configPrefix() + ".listen_host", XDBCBridgeHelper::DEFAULT_HOST) << ' '; command << "--http-timeout " << http_timeout.totalMicroseconds() << ' '; - if (config.has("logger.odbc_bridge_log")) + if (config.has("logger." + configPrefix() +"_log")) command << "--log-path " << config.getString("logger."+configPrefix()+"_log") << ' '; - if (config.has("logger.odbc_bridge_errlog")) + if (config.has("logger." + configPrefix() + "_errlog")) command << "--err-log-path " << config.getString("logger." + configPrefix() + "_errlog") << ' '; - if (config.has("logger.odbc_bridge_level")) + if (config.has("logger." + configPrefix() + "_level")) command << "--log-level " << config.getString("logger." + configPrefix() + "_level") << ' '; command << "&"; /// we don't want to wait this process auto command_str = command.str(); -// LOG_TRACE(log, "Starting " + serviceAlias() +" with command: " << command_str); + + std::cerr << command_str << std::endl; + + LOG_TRACE(log, "Starting " + serviceAlias() + " with command: " << command_str); auto cmd = ShellCommand::execute(command_str); cmd->wait(); diff --git a/dbms/src/Dictionaries/DictionarySourceFactory.cpp b/dbms/src/Dictionaries/DictionarySourceFactory.cpp index 82d8cb8f596..e507b503f63 100644 --- a/dbms/src/Dictionaries/DictionarySourceFactory.cpp +++ b/dbms/src/Dictionaries/DictionarySourceFactory.cpp @@ -7,10 +7,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -22,7 +24,6 @@ #endif #if USE_POCO_SQLODBC || USE_POCO_DATAODBC #include - #include #endif #if USE_MYSQL #include @@ -154,11 +155,19 @@ DictionarySourcePtr DictionarySourceFactory::create( else if ("odbc" == source_type) { #if USE_POCO_SQLODBC || USE_POCO_DATAODBC - return std::make_unique(dict_struct, config, config_prefix + ".odbc", sample_block, context); + BridgeHelperPtr bridge = std::make_shared>(config, context.getSettings().http_connection_timeout, config.getString(config_prefix + ".connection_string")); + return std::make_unique(dict_struct, config, config_prefix + ".odbc", sample_block, context, bridge); #else throw Exception{"Dictionary source of type `odbc` is disabled because poco library was built without ODBC support.", ErrorCodes::SUPPORT_IS_DISABLED}; #endif + } + else if ("jdbc" == source_type) + { + throw Exception{"Dictionary source of type `jdbc` is disabled until consistend dealing with nullable fields.", + ErrorCodes::SUPPORT_IS_DISABLED}; +// BridgeHelperPtr bridge = std::make_shared>(config, context.getSettings().http_connection_timeout, config.getString(config_prefix + ".connection_string")); +// return std::make_unique(dict_struct, config, config_prefix + ".jdbc", sample_block, context, bridge); } else if ("executable" == source_type) { diff --git a/dbms/src/Dictionaries/ODBCDictionarySource.h b/dbms/src/Dictionaries/ODBCDictionarySource.h deleted file mode 100644 index 7d7a0ca51e0..00000000000 --- a/dbms/src/Dictionaries/ODBCDictionarySource.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -#include -#include - -#include - -#include -#include -#include - -#include - - -namespace Poco -{ - namespace Util - { - class AbstractConfiguration; - } - - class Logger; -} - - -namespace DB -{ - - -/// Allows loading dictionaries from a ODBC source -class ODBCDictionarySource final : public IDictionarySource -{ -public: - ODBCDictionarySource(const DictionaryStructure & dict_struct_, - const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix, - const Block & sample_block, const Context & context); - - /// copy-constructor is provided in order to support cloneability - ODBCDictionarySource(const ODBCDictionarySource & other); - - BlockInputStreamPtr loadAll() override; - - BlockInputStreamPtr loadUpdatedAll() override; - - BlockInputStreamPtr loadIds(const std::vector & ids) override; - - BlockInputStreamPtr loadKeys( - const Columns & key_columns, const std::vector & requested_rows) override; - - bool isModified() const override; - - bool supportsSelectiveLoad() const override; - - bool hasUpdateField() const override; - - DictionarySourcePtr clone() const override; - - std::string toString() const override; - -private: - std::string getUpdateFieldAndDate(); - - // execute invalidate_query. expects single cell in result - std::string doInvalidateQuery(const std::string & request) const; - - BlockInputStreamPtr loadBase(const std::string & query) const; - - Poco::Logger * log; - - std::chrono::time_point update_time; - const DictionaryStructure dict_struct; - const std::string db; - const std::string table; - const std::string where; - const std::string update_field; - Block sample_block; - ExternalQueryBuilder query_builder; - const std::string load_all_query; - std::string invalidate_query; - mutable std::string invalidate_query_response; - - ODBCBridgeHelper odbc_bridge_helper; - Poco::URI bridge_url; - ConnectionTimeouts timeouts; - const Context & global_context; - -}; - - -} diff --git a/dbms/src/Dictionaries/ODBCDictionarySource.cpp b/dbms/src/Dictionaries/XDBCDictionarySource.cpp similarity index 65% rename from dbms/src/Dictionaries/ODBCDictionarySource.cpp rename to dbms/src/Dictionaries/XDBCDictionarySource.cpp index ae88c5ee2ea..0fb0a938551 100644 --- a/dbms/src/Dictionaries/ODBCDictionarySource.cpp +++ b/dbms/src/Dictionaries/XDBCDictionarySource.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -12,6 +12,7 @@ #include #include #include +#include namespace DB @@ -19,18 +20,18 @@ namespace DB namespace { - class ODBCBridgeBlockInputStream : public IProfilingBlockInputStream + class XDBCBridgeBlockInputStream : public IProfilingBlockInputStream { public: - ODBCBridgeBlockInputStream(const Poco::URI & uri, + XDBCBridgeBlockInputStream(const Poco::URI & uri, std::function callback, const Block & sample_block, const Context & context, size_t max_block_size, - const ConnectionTimeouts & timeouts) + const ConnectionTimeouts & timeouts, const String name) : name(name) { read_buf = std::make_unique(uri, Poco::Net::HTTPRequest::HTTP_POST, callback, timeouts); - reader = FormatFactory::instance().getInput(ODBCBridgeHelper::DEFAULT_FORMAT, *read_buf, sample_block, context, max_block_size); + reader = FormatFactory::instance().getInput(IXDBCBridgeHelper::DEFAULT_FORMAT, *read_buf, sample_block, context, max_block_size); } Block getHeader() const override @@ -40,7 +41,7 @@ namespace String getName() const override { - return "ODBCBridgeBlockInputStream"; + return name; } private: @@ -49,6 +50,7 @@ namespace return reader->read(); } + String name; std::unique_ptr read_buf; BlockInputStreamPtr reader; }; @@ -57,10 +59,10 @@ namespace static const size_t max_block_size = 8192; -ODBCDictionarySource::ODBCDictionarySource(const DictionaryStructure & dict_struct_, +XDBCDictionarySource::XDBCDictionarySource(const DictionaryStructure & dict_struct_, const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix, - const Block & sample_block, const Context & context) - : log(&Logger::get("ODBCDictionarySource")), + const Block & sample_block, const Context & context, const BridgeHelperPtr bridge) + : log(&Logger::get(bridge->getName() + "DictionarySource")), update_time{std::chrono::system_clock::from_time_t(0)}, dict_struct{dict_struct_}, db{config.getString(config_prefix + ".db", "")}, @@ -68,23 +70,23 @@ ODBCDictionarySource::ODBCDictionarySource(const DictionaryStructure & dict_stru where{config.getString(config_prefix + ".where", "")}, update_field{config.getString(config_prefix + ".update_field", "")}, sample_block{sample_block}, - query_builder{dict_struct, db, table, where, IdentifierQuotingStyle::None}, /// NOTE Better to obtain quoting style via ODBC interface. + query_builder{dict_struct, db, table, where, bridge->getIdentifierQuotingStyle()}, load_all_query{query_builder.composeLoadAllQuery()}, invalidate_query{config.getString(config_prefix + ".invalidate_query", "")}, - odbc_bridge_helper{context.getConfigRef(), context.getSettingsRef().http_receive_timeout.value, config.getString(config_prefix + ".connection_string")}, + bridge_helper{bridge}, timeouts{ConnectionTimeouts::getHTTPTimeouts(context.getSettingsRef())}, global_context(context) { - bridge_url = odbc_bridge_helper.getMainURI(); + bridge_url = bridge_helper->getMainURI(); - auto url_params = odbc_bridge_helper.getURLParams(sample_block.getNamesAndTypesList().toString(), max_block_size); + auto url_params = bridge_helper->getURLParams(sample_block.getNamesAndTypesList().toString(), max_block_size); for (const auto & [name, value] : url_params) bridge_url.addQueryParameter(name, value); } /// copy-constructor is provided in order to support cloneability -ODBCDictionarySource::ODBCDictionarySource(const ODBCDictionarySource & other) - : log(&Logger::get("ODBCDictionarySource")), +XDBCDictionarySource::XDBCDictionarySource(const XDBCDictionarySource & other) + : log(&Logger::get(other.bridge_helper->getName() + "DictionarySource")), update_time{other.update_time}, dict_struct{other.dict_struct}, db{other.db}, @@ -92,11 +94,11 @@ ODBCDictionarySource::ODBCDictionarySource(const ODBCDictionarySource & other) where{other.where}, update_field{other.update_field}, sample_block{other.sample_block}, - query_builder{dict_struct, db, table, where, IdentifierQuotingStyle::None}, + query_builder{dict_struct, db, table, where, other.bridge_helper->getIdentifierQuotingStyle()}, load_all_query{other.load_all_query}, invalidate_query{other.invalidate_query}, invalidate_query_response{other.invalidate_query_response}, - odbc_bridge_helper{other.odbc_bridge_helper}, + bridge_helper{other.bridge_helper}, bridge_url{other.bridge_url}, timeouts{other.timeouts}, global_context{other.global_context} @@ -104,7 +106,7 @@ ODBCDictionarySource::ODBCDictionarySource(const ODBCDictionarySource & other) } -std::string ODBCDictionarySource::getUpdateFieldAndDate() +std::string XDBCDictionarySource::getUpdateFieldAndDate() { if (update_time != std::chrono::system_clock::from_time_t(0)) { @@ -122,13 +124,13 @@ std::string ODBCDictionarySource::getUpdateFieldAndDate() } } -BlockInputStreamPtr ODBCDictionarySource::loadAll() +BlockInputStreamPtr XDBCDictionarySource::loadAll() { LOG_TRACE(log, load_all_query); return loadBase(load_all_query); } -BlockInputStreamPtr ODBCDictionarySource::loadUpdatedAll() +BlockInputStreamPtr XDBCDictionarySource::loadUpdatedAll() { std::string load_query_update = getUpdateFieldAndDate(); @@ -136,40 +138,40 @@ BlockInputStreamPtr ODBCDictionarySource::loadUpdatedAll() return loadBase(load_query_update); } -BlockInputStreamPtr ODBCDictionarySource::loadIds(const std::vector & ids) +BlockInputStreamPtr XDBCDictionarySource::loadIds(const std::vector & ids) { const auto query = query_builder.composeLoadIdsQuery(ids); return loadBase(query); } -BlockInputStreamPtr ODBCDictionarySource::loadKeys( +BlockInputStreamPtr XDBCDictionarySource::loadKeys( const Columns & key_columns, const std::vector & requested_rows) { const auto query = query_builder.composeLoadKeysQuery(key_columns, requested_rows, ExternalQueryBuilder::AND_OR_CHAIN); return loadBase(query); } -bool ODBCDictionarySource::supportsSelectiveLoad() const +bool XDBCDictionarySource::supportsSelectiveLoad() const { return true; } -bool ODBCDictionarySource::hasUpdateField() const +bool XDBCDictionarySource::hasUpdateField() const { return !update_field.empty(); } -DictionarySourcePtr ODBCDictionarySource::clone() const +DictionarySourcePtr XDBCDictionarySource::clone() const { - return std::make_unique(*this); + return std::make_unique(*this); } -std::string ODBCDictionarySource::toString() const +std::string XDBCDictionarySource::toString() const { - return "ODBC: " + db + '.' + table + (where.empty() ? "" : ", where: " + where); + return bridge_helper->getName() + ": " + db + '.' + table + (where.empty() ? "" : ", where: " + where); } -bool ODBCDictionarySource::isModified() const +bool XDBCDictionarySource::isModified() const { if (!invalidate_query.empty()) { @@ -182,39 +184,39 @@ bool ODBCDictionarySource::isModified() const } -std::string ODBCDictionarySource::doInvalidateQuery(const std::string & request) const +std::string XDBCDictionarySource::doInvalidateQuery(const std::string & request) const { Block invalidate_sample_block; ColumnPtr column(ColumnString::create()); invalidate_sample_block.insert(ColumnWithTypeAndName(column, std::make_shared(), "Sample Block")); - odbc_bridge_helper.startODBCBridgeSync(); + bridge_helper->startBridgeSync(); - auto invalidate_url = odbc_bridge_helper.getMainURI(); - auto url_params = odbc_bridge_helper.getURLParams(invalidate_sample_block.getNamesAndTypesList().toString(), max_block_size); + auto invalidate_url = bridge_helper->getMainURI(); + auto url_params = bridge_helper->getURLParams(invalidate_sample_block.getNamesAndTypesList().toString(), max_block_size); for (const auto & [name, value] : url_params) invalidate_url.addQueryParameter(name, value); - ODBCBridgeBlockInputStream stream( + XDBCBridgeBlockInputStream stream( invalidate_url, [request](std::ostream & os) { os << "query=" << request; }, invalidate_sample_block, global_context, max_block_size, - timeouts); + timeouts, bridge_helper->getName() + "BlockInputStream"); return readInvalidateQuery(stream); } -BlockInputStreamPtr ODBCDictionarySource::loadBase(const std::string & query) const +BlockInputStreamPtr XDBCDictionarySource::loadBase(const std::string & query) const { - odbc_bridge_helper.startODBCBridgeSync(); - return std::make_shared(bridge_url, + bridge_helper->startBridgeSync(); + return std::make_shared(bridge_url, [query](std::ostream & os) { os << "query=" << query; }, sample_block, global_context, max_block_size, - timeouts); + timeouts, bridge_helper->getName() + "BlockInputStream"); } } diff --git a/dbms/src/Dictionaries/XDBCDictionarySource.h b/dbms/src/Dictionaries/XDBCDictionarySource.h new file mode 100644 index 00000000000..b3c7bbc9d70 --- /dev/null +++ b/dbms/src/Dictionaries/XDBCDictionarySource.h @@ -0,0 +1,88 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include + + +namespace Poco +{ + namespace Util + { + class AbstractConfiguration; + } + + class Logger; +} + + +namespace DB +{ + +/// Allows loading dictionaries from a XDBC source via bridges + class XDBCDictionarySource final : public IDictionarySource + { + public: + XDBCDictionarySource(const DictionaryStructure & dict_struct_, + const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix, + const Block & sample_block, const Context & context, BridgeHelperPtr bridge); + + /// copy-constructor is provided in order to support cloneability + XDBCDictionarySource(const XDBCDictionarySource & other); + + BlockInputStreamPtr loadAll() override; + + BlockInputStreamPtr loadUpdatedAll() override; + + BlockInputStreamPtr loadIds(const std::vector & ids) override; + + BlockInputStreamPtr loadKeys( + const Columns & key_columns, const std::vector & requested_rows) override; + + bool isModified() const override; + + bool supportsSelectiveLoad() const override; + + bool hasUpdateField() const override; + + DictionarySourcePtr clone() const override; + + std::string toString() const override; + + private: + std::string getUpdateFieldAndDate(); + + // execute invalidate_query. expects single cell in result + std::string doInvalidateQuery(const std::string & request) const; + + BlockInputStreamPtr loadBase(const std::string & query) const; + + Poco::Logger * log; + + std::chrono::time_point update_time; + const DictionaryStructure dict_struct; + const std::string db; + const std::string table; + const std::string where; + const std::string update_field; + Block sample_block; + ExternalQueryBuilder query_builder; + const std::string load_all_query; + std::string invalidate_query; + mutable std::string invalidate_query_response; + + BridgeHelperPtr bridge_helper; + Poco::URI bridge_url; + ConnectionTimeouts timeouts; + const Context & global_context; + + }; + + +} diff --git a/dbms/src/Dictionaries/readInvalidateQuery.h b/dbms/src/Dictionaries/readInvalidateQuery.h index 90252a26eca..d2bffd57f95 100644 --- a/dbms/src/Dictionaries/readInvalidateQuery.h +++ b/dbms/src/Dictionaries/readInvalidateQuery.h @@ -6,7 +6,7 @@ class IProfilingBlockInputStream; namespace DB { -// Using in MySQLDictionarySource and ODBCDictionarySource after processing invalidate_query +// Using in MySQLDictionarySource and XDBCDictionarySource after processing invalidate_query std::string readInvalidateQuery(IProfilingBlockInputStream & block_input_stream); diff --git a/dbms/src/Storages/StorageODBC.cpp b/dbms/src/Storages/StorageODBC.cpp deleted file mode 100644 index 89eb5c9d1ce..00000000000 --- a/dbms/src/Storages/StorageODBC.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -namespace DB -{ -namespace ErrorCodes -{ - extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; - extern const int EXTERNAL_EXECUTABLE_NOT_FOUND; - extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING; -} - - -StorageODBC::StorageODBC(const std::string & table_name_, - const std::string & connection_string, - const std::string & remote_database_name_, - const std::string & remote_table_name_, - const ColumnsDescription & columns_, - const Context & context_) - : IStorageURLBase(Poco::URI(), context_, table_name_, ODBCBridgeHelper::DEFAULT_FORMAT, columns_) - , odbc_bridge_helper(context_global.getConfigRef(), context_global.getSettingsRef().http_receive_timeout.value, connection_string) - , remote_database_name(remote_database_name_) - , remote_table_name(remote_table_name_) - , log(&Poco::Logger::get("StorageODBC")) -{ - uri = odbc_bridge_helper.getMainURI(); -} - -std::string StorageODBC::getReadMethod() const -{ - return Poco::Net::HTTPRequest::HTTP_POST; -} - -std::vector> StorageODBC::getReadURIParams(const Names & column_names, - const SelectQueryInfo & /*query_info*/, - const Context & /*context*/, - QueryProcessingStage::Enum & /*processed_stage*/, - size_t max_block_size) const -{ - NamesAndTypesList cols; - for (const String & name : column_names) - { - auto column_data = getColumn(name); - cols.emplace_back(column_data.name, column_data.type); - } - return odbc_bridge_helper.getURLParams(cols.toString(), max_block_size); -} - -std::function StorageODBC::getReadPOSTDataCallback(const Names & /*column_names*/, - const SelectQueryInfo & query_info, - const Context & context, - QueryProcessingStage::Enum & /*processed_stage*/, - size_t /*max_block_size*/) const -{ - String query = transformQueryForExternalDatabase( - *query_info.query, getColumns().ordinary, IdentifierQuotingStyle::DoubleQuotes, remote_database_name, remote_table_name, context); - - return [query](std::ostream & os) { os << "query=" << query; }; -} - -BlockInputStreams StorageODBC::read(const Names & column_names, - const SelectQueryInfo & query_info, - const Context & context, - QueryProcessingStage::Enum processed_stage, - size_t max_block_size, - unsigned num_streams) -{ - check(column_names); - - odbc_bridge_helper.startODBCBridgeSync(); - return IStorageURLBase::read(column_names, query_info, context, processed_stage, max_block_size, num_streams); -} - - -Block StorageODBC::getHeaderBlock(const Names & column_names) const -{ - return getSampleBlockForColumns(column_names); -} - -void registerStorageODBC(StorageFactory & factory) -{ - factory.registerStorage("ODBC", [](const StorageFactory::Arguments & args) - { - ASTs & engine_args = args.engine_args; - - if (engine_args.size() != 3) - throw Exception( - "Storage ODBC requires exactly 3 parameters: ODBC('DSN', database or schema, table)", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - for (size_t i = 0; i < 3; ++i) - engine_args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[i], args.local_context); - - return StorageODBC::create(args.table_name, - static_cast(*engine_args[0]).value.safeGet(), - static_cast(*engine_args[1]).value.safeGet(), - static_cast(*engine_args[2]).value.safeGet(), - args.columns, - args.context); - }); -} -} diff --git a/dbms/src/Storages/StorageODBC.h b/dbms/src/Storages/StorageODBC.h deleted file mode 100644 index 29d30b4a688..00000000000 --- a/dbms/src/Storages/StorageODBC.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace DB -{ -/** Implements storage in the ODBC database. - * Use ENGINE = odbc(connection_string, table_name) - * Example ENGINE = odbc('dsn=test', table) - * Read only. - */ -class StorageODBC : public ext::shared_ptr_helper, public IStorageURLBase -{ -public: - std::string getName() const override - { - return "ODBC"; - } - - BlockInputStreams read(const Names & column_names, - const SelectQueryInfo & query_info, - const Context & context, - QueryProcessingStage::Enum processed_stage, - size_t max_block_size, - unsigned num_streams) override; - - -protected: - StorageODBC(const std::string & table_name_, - const std::string & connection_string, - const std::string & remote_database_name, - const std::string & remote_table_name, - const ColumnsDescription & columns_, - const Context & context_); - -private: - ODBCBridgeHelper odbc_bridge_helper; - std::string remote_database_name; - std::string remote_table_name; - - Poco::Logger * log; - - std::string getReadMethod() const override; - - std::vector> getReadURIParams(const Names & column_names, - const SelectQueryInfo & query_info, - const Context & context, - QueryProcessingStage::Enum & processed_stage, - size_t max_block_size) const override; - - std::function getReadPOSTDataCallback(const Names & column_names, - const SelectQueryInfo & query_info, - const Context & context, - QueryProcessingStage::Enum & processed_stage, - size_t max_block_size) const override; - - Block getHeaderBlock(const Names & column_names) const override; -}; -} diff --git a/dbms/src/Storages/StorageXDBC.cpp b/dbms/src/Storages/StorageXDBC.cpp index d25c903fdd9..5a3e7623e61 100644 --- a/dbms/src/Storages/StorageXDBC.cpp +++ b/dbms/src/Storages/StorageXDBC.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -20,8 +19,6 @@ namespace DB namespace ErrorCodes { extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; - extern const int EXTERNAL_EXECUTABLE_NOT_FOUND; - extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING; } @@ -36,8 +33,7 @@ namespace DB , remote_database_name(remote_database_name_) , remote_table_name(remote_table_name_) { - // @todo give it appropriate name - log = &Poco::Logger::get("StorageXDBC"); + log = &Poco::Logger::get("Storage" + bridge_helper->getName()); uri = bridge_helper->getMainURI(); } @@ -92,62 +88,53 @@ namespace DB return getSampleBlockForColumns(column_names); } - void registerStorageJDBC(StorageFactory & factory) + std::string StorageXDBC::getName() const { - factory.registerStorage("JDBC", [](const StorageFactory::Arguments & args) - { - ASTs & engine_args = args.engine_args; - - if (engine_args.size() != 3) - throw Exception( - "Storage JDBC requires exactly 3 parameters: JDBC('DSN', database or schema, table)", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - for (size_t i = 0; i < 3; ++i) - engine_args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[i], args.local_context); - - BridgeHelperPtr bridge_helper = std::make_shared>( - args.context.getConfigRef(), - args.context.getSettingsRef().http_receive_timeout.value, - static_cast(*engine_args[0]).value.safeGet() - ); - return std::make_shared(args.table_name, - static_cast(*engine_args[1]).value.safeGet(), - static_cast(*engine_args[2]).value.safeGet(), - args.columns, - args.context, - bridge_helper - ); - - }); + return bridge_helper->getName(); } - void registerStorageIDBC(StorageFactory & factory) + namespace { - factory.registerStorage("IDBC", [](const StorageFactory::Arguments & args) + + template + void registerXDBCStorage(StorageFactory & factory, const std::string & name) { - ASTs & engine_args = args.engine_args; + factory.registerStorage(name, [&name](const StorageFactory::Arguments & args) + { + ASTs & engine_args = args.engine_args; - if (engine_args.size() != 3) - throw Exception( - "Storage IDBC requires exactly 3 parameters: IDBC('DSN', database or schema, table)", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + if (engine_args.size() != 3) + throw Exception( + "Storage " + name + " requires exactly 3 parameters: " + name + "('DSN', database or schema, table)", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - for (size_t i = 0; i < 3; ++i) - engine_args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[i], args.local_context); + for (size_t i = 0; i < 3; ++i) + engine_args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[i], args.local_context); - BridgeHelperPtr bridge_helper = std::make_shared>( - args.context.getConfigRef(), - args.context.getSettingsRef().http_receive_timeout.value, - static_cast(*engine_args[0]).value.safeGet() - ); - return std::make_shared(args.table_name, - static_cast(*engine_args[1]).value.safeGet(), - static_cast(*engine_args[2]).value.safeGet(), - args.columns, - args.context, - bridge_helper - ); + BridgeHelperPtr bridge_helper = std::make_shared>( + args.context.getConfigRef(), + args.context.getSettingsRef().http_receive_timeout.value, + static_cast(*engine_args[0]).value.safeGet() + ); + return std::make_shared(args.table_name, + static_cast(*engine_args[1]).value.safeGet(), + static_cast(*engine_args[2]).value.safeGet(), + args.columns, + args.context, + bridge_helper + ); - }); + }); + } + } + + void registerStorageJDBC(StorageFactory & factory) + { + registerXDBCStorage(factory, "JDBC"); + } + + void registerStorageODBC(StorageFactory & factory) + { + registerXDBCStorage(factory, "ODBC"); } } diff --git a/dbms/src/Storages/StorageXDBC.h b/dbms/src/Storages/StorageXDBC.h index 8a522d33ec7..749e8910a24 100644 --- a/dbms/src/Storages/StorageXDBC.h +++ b/dbms/src/Storages/StorageXDBC.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -53,38 +52,7 @@ namespace DB size_t max_block_size) const override; Block getHeaderBlock(const Names & column_names) const override; - }; - // Implementations for StorageXDBC - class StorageJDBC : public StorageXDBC - { - public: - StorageJDBC(const std::string & table_name_, - const std::string & remote_database_name, - const std::string & remote_table_name, - const ColumnsDescription & columns_, - const Context & context_, BridgeHelperPtr bridge_helper_) - : StorageXDBC(table_name_, remote_database_name, remote_table_name, columns_, context_, bridge_helper_) {} - - std::string getName() const override - { - return "JDBC"; - } - }; - - class StorageIDBC : public StorageXDBC - { - public: - StorageIDBC(const std::string & table_name_, - const std::string & remote_database_name, - const std::string & remote_table_name, - const ColumnsDescription & columns_, - const Context & context_, BridgeHelperPtr bridge_helper_) - : StorageXDBC(table_name_, remote_database_name, remote_table_name, columns_, context_, bridge_helper_) {} - - std::string getName() const override - { - return "IDBC"; - } + std::string getName() const override; }; } diff --git a/dbms/src/Storages/registerStorages.cpp b/dbms/src/Storages/registerStorages.cpp index eef34aa0c82..ce831fbb758 100644 --- a/dbms/src/Storages/registerStorages.cpp +++ b/dbms/src/Storages/registerStorages.cpp @@ -26,7 +26,6 @@ void registerStorageMaterializedView(StorageFactory & factory); #if USE_POCO_SQLODBC || USE_POCO_DATAODBC void registerStorageODBC(StorageFactory & factory); -void registerStorageIDBC(StorageFactory & factory); #endif void registerStorageJDBC(StorageFactory & factory); @@ -63,7 +62,6 @@ void registerStorages() #if USE_POCO_SQLODBC || USE_POCO_DATAODBC registerStorageODBC(factory); - registerStorageIDBC(factory); #endif registerStorageJDBC(factory); diff --git a/dbms/src/TableFunctions/ITableFunctionXDBC.cpp b/dbms/src/TableFunctions/ITableFunctionXDBC.cpp index ce7e02a4071..482cc605810 100644 --- a/dbms/src/TableFunctions/ITableFunctionXDBC.cpp +++ b/dbms/src/TableFunctions/ITableFunctionXDBC.cpp @@ -1,4 +1,3 @@ -#include #include #include @@ -11,13 +10,12 @@ #include #include #include -#include #include #include #include +#include #include #include -#include #include @@ -75,7 +73,7 @@ namespace DB readStringBinary(columns_info, buf); NamesAndTypesList columns = NamesAndTypesList::parse(columns_info); - auto result = createStorage(table_name, schema_name, table_name, ColumnsDescription{columns}, context, helper); + auto result = std::make_shared(table_name, schema_name, table_name, ColumnsDescription{columns}, context, helper); if(!result) throw Exception("Failed to instantiate storage from table function " + getName()); @@ -89,8 +87,8 @@ namespace DB factory.registerFunction(); } - void registerTableFunctionIDBC(TableFunctionFactory & factory) + void registerTableFunctionODBC(TableFunctionFactory & factory) { - factory.registerFunction(); + factory.registerFunction(); } } diff --git a/dbms/src/TableFunctions/ITableFunctionXDBC.h b/dbms/src/TableFunctions/ITableFunctionXDBC.h index 1fde772a187..055d4db022b 100644 --- a/dbms/src/TableFunctions/ITableFunctionXDBC.h +++ b/dbms/src/TableFunctions/ITableFunctionXDBC.h @@ -17,14 +17,6 @@ class ITableFunctionXDBC : public ITableFunction private: StoragePtr executeImpl(const ASTPtr & ast_function, const Context & context) const override; - /* A factory method for creating the storage implementation */ - virtual StoragePtr createStorage(const std::string & table_name_, - const std::string & remote_database_name, - const std::string & remote_table_name, - const ColumnsDescription & columns_, - const Context & context_, - BridgeHelperPtr bridge_helper) const = 0; - /* A factory method to create bridge helper, that will assist in remote interaction */ virtual BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, const Poco::Timespan & http_timeout_, @@ -40,14 +32,6 @@ public: return name; } private: - StoragePtr createStorage(const std::string &table_name_, const std::string &remote_database_name, - const std::string &remote_table_name, - const ColumnsDescription &columns_, - const Context &context_, BridgeHelperPtr bridge_helper) const override { - - return std::make_shared(table_name_, remote_database_name, remote_table_name, columns_, context_, bridge_helper); - } - BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, const Poco::Timespan & http_timeout_, @@ -56,23 +40,15 @@ private: } }; -class TableFunctionIDBC : public ITableFunctionXDBC +class TableFunctionODBC : public ITableFunctionXDBC { public: - static constexpr auto name = "idbc"; + static constexpr auto name = "odbc"; std::string getName() const override { return name; } private: - StoragePtr createStorage(const std::string &table_name_, const std::string &remote_database_name, - const std::string &remote_table_name, - const ColumnsDescription &columns_, - const Context &context_, BridgeHelperPtr bridge_helper) const override { - - return std::make_shared(table_name_, remote_database_name, remote_table_name, columns_, context_, bridge_helper); - } - BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, const Poco::Timespan & http_timeout_, diff --git a/dbms/src/TableFunctions/TableFunctionODBC.cpp b/dbms/src/TableFunctions/TableFunctionODBC.cpp deleted file mode 100644 index a29f72412db..00000000000 --- a/dbms/src/TableFunctions/TableFunctionODBC.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; -} - -StoragePtr TableFunctionODBC::executeImpl(const ASTPtr & ast_function, const Context & context) const -{ - const ASTFunction & args_func = typeid_cast(*ast_function); - - if (!args_func.arguments) - throw Exception("Table function 'odbc' must have arguments.", ErrorCodes::LOGICAL_ERROR); - - ASTs & args = typeid_cast(*args_func.arguments).children; - if (args.size() != 2 && args.size() != 3) - throw Exception("Table function 'odbc' requires 2 or 3 arguments: ODBC('DSN', table) or ODBC('DSN', schema, table)", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - for (auto i = 0u; i < args.size(); ++i) - args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(args[i], context); - - std::string connection_string = ""; - std::string schema_name = ""; - std::string table_name = ""; - if (args.size() == 3) - { - connection_string = static_cast(*args[0]).value.safeGet(); - schema_name = static_cast(*args[1]).value.safeGet(); - table_name = static_cast(*args[2]).value.safeGet(); - } else if (args.size() == 2) - { - connection_string = static_cast(*args[0]).value.safeGet(); - table_name = static_cast(*args[1]).value.safeGet(); - } - - const auto & config = context.getConfigRef(); - ODBCBridgeHelper helper(config, context.getSettingsRef().http_receive_timeout.value, connection_string); - helper.startODBCBridgeSync(); - - Poco::URI columns_info_uri = helper.getColumnsInfoURI(); - columns_info_uri.addQueryParameter("connection_string", connection_string); - if (!schema_name.empty()) - columns_info_uri.addQueryParameter("schema", schema_name); - columns_info_uri.addQueryParameter("table", table_name); - - ReadWriteBufferFromHTTP buf(columns_info_uri, Poco::Net::HTTPRequest::HTTP_POST, nullptr); - - std::string columns_info; - readStringBinary(columns_info, buf); - NamesAndTypesList columns = NamesAndTypesList::parse(columns_info); - - auto result = StorageODBC::create(table_name, connection_string, schema_name, table_name, ColumnsDescription{columns}, context); - result->startup(); - return result; -} - - -void registerTableFunctionODBC(TableFunctionFactory & factory) -{ - factory.registerFunction(); -} -} diff --git a/dbms/src/TableFunctions/TableFunctionODBC.h b/dbms/src/TableFunctions/TableFunctionODBC.h deleted file mode 100644 index 9417ac0e972..00000000000 --- a/dbms/src/TableFunctions/TableFunctionODBC.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include - - -namespace DB -{ -/* odbc (odbc connect string, table) - creates a temporary StorageODBC. - */ -class TableFunctionODBC : public ITableFunction -{ -public: - static constexpr auto name = "odbc"; - std::string getName() const override - { - return name; - } -private: - StoragePtr executeImpl(const ASTPtr & ast_function, const Context & context) const override; -}; -} diff --git a/dbms/src/TableFunctions/registerTableFunctions.cpp b/dbms/src/TableFunctions/registerTableFunctions.cpp index 71ce092b398..d0afeff0b17 100644 --- a/dbms/src/TableFunctions/registerTableFunctions.cpp +++ b/dbms/src/TableFunctions/registerTableFunctions.cpp @@ -16,7 +16,6 @@ void registerTableFunctionURL(TableFunctionFactory & factory); #if USE_POCO_SQLODBC || USE_POCO_DATAODBC void registerTableFunctionODBC(TableFunctionFactory & factory); -void registerTableFunctionIDBC(TableFunctionFactory & factory); #endif void registerTableFunctionJDBC(TableFunctionFactory & factory); @@ -40,7 +39,6 @@ void registerTableFunctions() #if USE_POCO_SQLODBC || USE_POCO_DATAODBC registerTableFunctionODBC(factory); - registerTableFunctionIDBC(factory); #endif registerTableFunctionJDBC(factory); From 669ab715020b05054e69f29f3e1bc7277d89671f Mon Sep 17 00:00:00 2001 From: chertus Date: Fri, 28 Sep 2018 13:52:08 +0300 Subject: [PATCH 15/78] rename files according to class name --- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 2 +- dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp | 2 +- .../{getQueryAliases.cpp => QueryAliasesVisitor.cpp} | 2 +- .../Interpreters/{getQueryAliases.h => QueryAliasesVisitor.h} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename dbms/src/Interpreters/{getQueryAliases.cpp => QueryAliasesVisitor.cpp} (98%) rename dbms/src/Interpreters/{getQueryAliases.h => QueryAliasesVisitor.h} (100%) diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index a8ed01efbe7..38a91c1f72c 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -69,7 +69,7 @@ #include #include #include -#include +#include #include diff --git a/dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp b/dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp index 3bcb6ba841c..a53b72f3b86 100644 --- a/dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp +++ b/dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include namespace DB { diff --git a/dbms/src/Interpreters/getQueryAliases.cpp b/dbms/src/Interpreters/QueryAliasesVisitor.cpp similarity index 98% rename from dbms/src/Interpreters/getQueryAliases.cpp rename to dbms/src/Interpreters/QueryAliasesVisitor.cpp index c3387dbd018..0868e53d4e5 100644 --- a/dbms/src/Interpreters/getQueryAliases.cpp +++ b/dbms/src/Interpreters/QueryAliasesVisitor.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include diff --git a/dbms/src/Interpreters/getQueryAliases.h b/dbms/src/Interpreters/QueryAliasesVisitor.h similarity index 100% rename from dbms/src/Interpreters/getQueryAliases.h rename to dbms/src/Interpreters/QueryAliasesVisitor.h From b4ea89e1f3e47a8f6d014f3107cdeb3d48ed6dbc Mon Sep 17 00:00:00 2001 From: chertus Date: Fri, 28 Sep 2018 14:46:52 +0300 Subject: [PATCH 16/78] minor fix --- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 11 ++++++-- .../TranslateQualifiedNamesVisitor.cpp | 17 +++---------- .../TranslateQualifiedNamesVisitor.h | 25 ++++++++++--------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 38a91c1f72c..d4e4d5b2821 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -73,6 +73,13 @@ #include +#if 0 +constexpr std::ostream * debug_ast_stream = &std::cout; +#else +constexpr std::ostream * debug_ast_stream = nullptr; +#endif + + namespace DB { @@ -230,7 +237,7 @@ ExpressionAnalyzer::ExpressionAnalyzer( LogicalExpressionsOptimizer(select_query, settings).perform(); /// Creates a dictionary `aliases`: alias -> ASTPtr - QueryAliasesVisitor queryAliasesVisitor; + QueryAliasesVisitor queryAliasesVisitor(debug_ast_stream); queryAliasesVisitor.visit(query, aliases); /// Common subexpression elimination. Rewrite rules. @@ -314,7 +321,7 @@ void ExpressionAnalyzer::translateQualifiedNames() for (const auto & table_expression : tables_expression) tables.emplace_back(getTableNameWithAliasFromTableExpression(table_expression, context)); - TranslateQualifiedNamesVisitor visitor(source_columns, tables); + TranslateQualifiedNamesVisitor visitor(source_columns, tables, debug_ast_stream); visitor.visit(query); } diff --git a/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp index adc8761372b..bd2b6c162cc 100644 --- a/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp +++ b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -15,10 +14,8 @@ namespace ErrorCodes extern const int UNKNOWN_IDENTIFIER; } -void TranslateQualifiedNamesVisitor::visit(ASTIdentifier * identifier, ASTPtr & ast) const +void TranslateQualifiedNamesVisitor::visit(ASTIdentifier * identifier, ASTPtr & ast, const DumpASTNode & dump) const { - DumpASTNode dump(*identifier, ostr, visit_depth, visitor_label); - if (identifier->general()) { /// Select first table name with max number of qualifiers which can be stripped. @@ -49,10 +46,8 @@ void TranslateQualifiedNamesVisitor::visit(ASTIdentifier * identifier, ASTPtr & } } -void TranslateQualifiedNamesVisitor::visit(ASTQualifiedAsterisk *, ASTPtr & ast) const +void TranslateQualifiedNamesVisitor::visit(ASTQualifiedAsterisk *, ASTPtr & ast, const DumpASTNode &) const { - DumpASTNode dump(*ast, ostr, visit_depth, visitor_label); - if (ast->children.size() != 1) throw Exception("Logical error: qualified asterisk must have exactly one child", ErrorCodes::LOGICAL_ERROR); @@ -82,19 +77,15 @@ void TranslateQualifiedNamesVisitor::visit(ASTQualifiedAsterisk *, ASTPtr & ast) throw Exception("Unknown qualified identifier: " + ident->getAliasOrColumnName(), ErrorCodes::UNKNOWN_IDENTIFIER); } -void TranslateQualifiedNamesVisitor::visit(ASTTableJoin * join, ASTPtr &) const +void TranslateQualifiedNamesVisitor::visit(ASTTableJoin * join, ASTPtr &, const DumpASTNode &) const { - DumpASTNode dump(*join, ostr, visit_depth, visitor_label); - /// Don't translate on_expression here in order to resolve equation parts later. if (join->using_expression_list) visit(join->using_expression_list); } -void TranslateQualifiedNamesVisitor::visit(ASTSelectQuery * select, ASTPtr & ast) const +void TranslateQualifiedNamesVisitor::visit(ASTSelectQuery * select, ASTPtr & ast, const DumpASTNode &) const { - DumpASTNode dump(*select, ostr, visit_depth, visitor_label); - /// If the WHERE clause or HAVING consists of a single quailified column, the reference must be translated not only in children, /// but also in where_expression and having_expression. if (select->prewhere_expression) diff --git a/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.h b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.h index 88246f71a83..dfe0fb17808 100644 --- a/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.h +++ b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.h @@ -2,6 +2,7 @@ #include #include +#include #include namespace DB @@ -18,8 +19,6 @@ class NamesAndTypesList; class TranslateQualifiedNamesVisitor { public: - static constexpr const char * visitor_label = "translateQualifiedNames"; - TranslateQualifiedNamesVisitor(const NamesAndTypesList & source_columns_, const std::vector & tables_, std::ostream * ostr_ = nullptr) : source_columns(source_columns_), @@ -30,10 +29,12 @@ public: void visit(ASTPtr & ast) const { - if (!tryVisit(ast) && - !tryVisit(ast) && - !tryVisit(ast) && - !tryVisit(ast)) + DumpASTNode dump(*ast, ostr, visit_depth, "translateQualifiedNames"); + + if (!tryVisit(ast, dump) && + !tryVisit(ast, dump) && + !tryVisit(ast, dump) && + !tryVisit(ast, dump)) visitChildren(ast); /// default: do nothing, visit children } @@ -43,19 +44,19 @@ private: mutable size_t visit_depth; std::ostream * ostr; - void visit(ASTIdentifier * node, ASTPtr & ast) const; - void visit(ASTQualifiedAsterisk * node, ASTPtr & ast) const; - void visit(ASTTableJoin * node, ASTPtr & ast) const; - void visit(ASTSelectQuery * ast, ASTPtr &) const; + void visit(ASTIdentifier * node, ASTPtr & ast, const DumpASTNode & dump) const; + void visit(ASTQualifiedAsterisk * node, ASTPtr & ast, const DumpASTNode & dump) const; + void visit(ASTTableJoin * node, ASTPtr & ast, const DumpASTNode & dump) const; + void visit(ASTSelectQuery * ast, ASTPtr &, const DumpASTNode & dump) const; void visitChildren(ASTPtr &) const; template - bool tryVisit(ASTPtr & ast) const + bool tryVisit(ASTPtr & ast, const DumpASTNode & dump) const { if (T * t = typeid_cast(ast.get())) { - visit(t, ast); + visit(t, ast, dump); return true; } return false; From 593b3ae3388d30efce112a4bfa37ce5beb334468 Mon Sep 17 00:00:00 2001 From: chertus Date: Fri, 28 Sep 2018 15:13:01 +0300 Subject: [PATCH 17/78] one more minor fix --- dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp index bd2b6c162cc..6b2cce57321 100644 --- a/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp +++ b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp @@ -34,7 +34,11 @@ void TranslateQualifiedNamesVisitor::visit(ASTIdentifier * identifier, ASTPtr & } } - stripIdentifier(ast, max_num_qualifiers_to_strip); + if (max_num_qualifiers_to_strip) + { + dump.print(String("stripIdentifier ") + identifier->name, max_num_qualifiers_to_strip); + stripIdentifier(ast, max_num_qualifiers_to_strip); + } /// In case if column from the joined table are in source columns, change it's name to qualified. if (best_table_pos && source_columns.contains(ast->getColumnName())) From 59df909f87ef0926ecafb480f8aae2a43a9eb388 Mon Sep 17 00:00:00 2001 From: chertus Date: Fri, 28 Sep 2018 18:01:13 +0300 Subject: [PATCH 18/78] more ast debug info --- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 26 ++++++++++++--- .../PredicateExpressionsOptimizer.cpp | 9 ++++-- .../TranslateQualifiedNamesVisitor.cpp | 2 ++ .../TranslateQualifiedNamesVisitor.h | 2 ++ dbms/src/Interpreters/evaluateQualified.cpp | 4 +-- dbms/src/Interpreters/evaluateQualified.h | 5 +-- dbms/src/Parsers/IAST.h | 32 ++++++++++++------- 7 files changed, 57 insertions(+), 23 deletions(-) diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index d4e4d5b2821..0fe09d38718 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -319,7 +319,23 @@ void ExpressionAnalyzer::translateQualifiedNames() std::vector tables_expression = getTableExpressions(query); for (const auto & table_expression : tables_expression) - tables.emplace_back(getTableNameWithAliasFromTableExpression(table_expression, context)); + { + auto table = getTableNameWithAliasFromTableExpression(table_expression, context.getCurrentDatabase()); + + { /// debug print + size_t depth = 0; + DumpASTNode dump(table_expression, debug_ast_stream, depth, "getTableNames"); + if (table_expression.database_and_table_name) + DumpASTNode(*table_expression.database_and_table_name, debug_ast_stream, depth); + if (table_expression.table_function) + DumpASTNode(*table_expression.table_function, debug_ast_stream, depth); + if (table_expression.subquery) + DumpASTNode(*table_expression.subquery, debug_ast_stream, depth); + dump.print("getTableNameWithAlias", table.database + '.' + table.table + ' ' + table.alias); + } + + tables.emplace_back(table); + } TranslateQualifiedNamesVisitor visitor(source_columns, tables, debug_ast_stream); visitor.visit(query); @@ -842,7 +858,7 @@ void ExpressionAnalyzer::normalizeTree() for (const auto & table_expression : tables_expression) { - const auto table_name = getTableNameWithAliasFromTableExpression(table_expression, context); + const auto table_name = getTableNameWithAliasFromTableExpression(table_expression, context.getCurrentDatabase()); NamesAndTypesList names_and_types = getNamesAndTypeListFromTableExpression(table_expression, context); table_names_nad_columns_name.emplace_back(std::pair(table_name, names_and_types.getNames())); } @@ -2819,8 +2835,8 @@ void ExpressionAnalyzer::collectJoinedColumnsFromJoinOnExpr() const auto & left_table_expression = static_cast(*left_tables_element->table_expression); const auto & right_table_expression = static_cast(*right_tables_element->table_expression); - auto left_source_names = getTableNameWithAliasFromTableExpression(left_table_expression, context); - auto right_source_names = getTableNameWithAliasFromTableExpression(right_table_expression, context); + auto left_source_names = getTableNameWithAliasFromTableExpression(left_table_expression, context.getCurrentDatabase()); + auto right_source_names = getTableNameWithAliasFromTableExpression(right_table_expression, context.getCurrentDatabase()); /// Stores examples of columns which are only from one table. struct TableBelonging @@ -2969,7 +2985,7 @@ void ExpressionAnalyzer::collectJoinedColumns(NameSet & joined_columns) const auto & table_join = static_cast(*node->table_join); const auto & table_expression = static_cast(*node->table_expression); - auto joined_table_name = getTableNameWithAliasFromTableExpression(table_expression, context); + auto joined_table_name = getTableNameWithAliasFromTableExpression(table_expression, context.getCurrentDatabase()); auto add_name_to_join_keys = [](Names & join_keys, ASTs & join_asts, const String & name, const ASTPtr & ast) { diff --git a/dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp b/dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp index a53b72f3b86..cb2c56dccf8 100644 --- a/dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp +++ b/dbms/src/Interpreters/PredicateExpressionsOptimizer.cpp @@ -47,7 +47,8 @@ bool PredicateExpressionsOptimizer::optimizeImpl( std::vector tables_expression = getSelectTablesExpression(ast_select); std::vector database_and_table_with_aliases; for (const auto & table_expression : tables_expression) - database_and_table_with_aliases.emplace_back(getTableNameWithAliasFromTableExpression(*table_expression, context)); + database_and_table_with_aliases.emplace_back( + getTableNameWithAliasFromTableExpression(*table_expression, context.getCurrentDatabase())); bool is_rewrite_subquery = false; for (const auto & outer_predicate : outer_predicate_expressions) @@ -265,7 +266,8 @@ void PredicateExpressionsOptimizer::getAllSubqueryProjectionColumns(SubqueriesPr if (table_expression->subquery) { /// Use qualifiers to translate the columns of subqueries - const auto database_and_table_with_alias = getTableNameWithAliasFromTableExpression(*table_expression, context); + const auto database_and_table_with_alias = + getTableNameWithAliasFromTableExpression(*table_expression, context.getCurrentDatabase()); String qualified_name_prefix = database_and_table_with_alias.getQualifiedNamePrefix(); getSubqueryProjectionColumns(all_subquery_projection_columns, qualified_name_prefix, static_cast(table_expression->subquery.get())->children[0]); @@ -350,7 +352,8 @@ ASTs PredicateExpressionsOptimizer::evaluateAsterisk(ASTSelectQuery * select_que for (auto it = tables_expression.begin(); it != tables_expression.end(); ++it) { const ASTTableExpression * table_expression = *it; - const auto database_and_table_with_alias = getTableNameWithAliasFromTableExpression(*table_expression, context); + const auto database_and_table_with_alias = + getTableNameWithAliasFromTableExpression(*table_expression, context.getCurrentDatabase()); /// database.table.* if (num_components == 2 && !database_and_table_with_alias.database.empty() && static_cast(*ident->children[0]).name == database_and_table_with_alias.database diff --git a/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp index 6b2cce57321..5ee99318c68 100644 --- a/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp +++ b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.cpp @@ -1,5 +1,7 @@ #include +#include + #include #include #include diff --git a/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.h b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.h index dfe0fb17808..8bf6e3120a1 100644 --- a/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.h +++ b/dbms/src/Interpreters/TranslateQualifiedNamesVisitor.h @@ -1,3 +1,5 @@ +#pragma once + #include #include diff --git a/dbms/src/Interpreters/evaluateQualified.cpp b/dbms/src/Interpreters/evaluateQualified.cpp index 068e1896130..c3e9cde0f24 100644 --- a/dbms/src/Interpreters/evaluateQualified.cpp +++ b/dbms/src/Interpreters/evaluateQualified.cpp @@ -48,7 +48,7 @@ void stripIdentifier(DB::ASTPtr & ast, size_t num_qualifiers_to_strip) DatabaseAndTableWithAlias getTableNameWithAliasFromTableExpression(const ASTTableExpression & table_expression, - const Context & context) + const String & current_database) { DatabaseAndTableWithAlias database_and_table_with_alias; @@ -60,7 +60,7 @@ DatabaseAndTableWithAlias getTableNameWithAliasFromTableExpression(const ASTTabl if (table_expression.database_and_table_name->children.empty()) { - database_and_table_with_alias.database = context.getCurrentDatabase(); + database_and_table_with_alias.database = current_database; database_and_table_with_alias.table = identifier.name; } else diff --git a/dbms/src/Interpreters/evaluateQualified.h b/dbms/src/Interpreters/evaluateQualified.h index 32d338f8de6..94833190d81 100644 --- a/dbms/src/Interpreters/evaluateQualified.h +++ b/dbms/src/Interpreters/evaluateQualified.h @@ -1,6 +1,7 @@ #pragma once -#include +#include +#include namespace DB { @@ -28,7 +29,7 @@ struct DatabaseAndTableWithAlias void stripIdentifier(DB::ASTPtr & ast, size_t num_qualifiers_to_strip); DatabaseAndTableWithAlias getTableNameWithAliasFromTableExpression(const ASTTableExpression & table_expression, - const Context & context); + const String & current_database); size_t getNumComponentsToStripInOrderToTranslateQualifiedName(const ASTIdentifier & identifier, const DatabaseAndTableWithAlias & names); diff --git a/dbms/src/Parsers/IAST.h b/dbms/src/Parsers/IAST.h index 6da6e5503f7..80635468145 100644 --- a/dbms/src/Parsers/IAST.h +++ b/dbms/src/Parsers/IAST.h @@ -237,17 +237,8 @@ public: (*ostr) << "-- " << (label ? label : "ast") << std::endl; ++visit_depth; - String id = ast.getID(); - std::replace(id.begin(), id.end(), '_', ' '); - (*ostr) << String(indent, ' ') << id; - - id = ast.tryGetAlias(); - if (!id.empty()) - print("alias", id, " "); - - if (!ast.children.empty()) - print("/", ast.children.size(), " "); /// slash is just a short name for 'children' here - + (*ostr) << String(indent, ' '); + printNode(); (*ostr) << std::endl; } @@ -279,6 +270,25 @@ private: std::ostream * ostr; size_t indent; size_t & visit_depth; /// shared with children + + String nodeId() const + { + String id = ast.getID(); + std::replace(id.begin(), id.end(), '_', ' '); + return id; + } + + void printNode() const + { + (*ostr) << nodeId(); + + String aslias = ast.tryGetAlias(); + if (!aslias.empty()) + print("alias", aslias, " "); + + if (!ast.children.empty()) + print("/", ast.children.size(), " "); /// slash is just a short name for 'children' here + } }; inline void dumpAST(const IAST & ast, std::ostream & ostr, DumpASTNode * parent = nullptr) From c3f10e76fcb6f9af137fc9fdb7fda357adb9da5f Mon Sep 17 00:00:00 2001 From: Alexandr Krasheninnikov Date: Mon, 1 Oct 2018 10:45:59 +0300 Subject: [PATCH 19/78] Add doc for jdbc table function --- .../ru/query_language/table_functions/jdbc.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 docs/ru/query_language/table_functions/jdbc.md diff --git a/docs/ru/query_language/table_functions/jdbc.md b/docs/ru/query_language/table_functions/jdbc.md new file mode 100644 index 00000000000..1d8721e25a5 --- /dev/null +++ b/docs/ru/query_language/table_functions/jdbc.md @@ -0,0 +1,23 @@ + + +# jdbc + +`jdbc(jdbc_connection_uri, schema, table)` - возвращает таблицу, соединение с которой происходит через JDBC-драйвер. + +Для работы этой табличной функциии требуется отдельно запускать приложение clickhouse-jdbc-bridge. +В отличии от табличной функции `odbc`, данная функция поддерживает Nullable типы (на основании DDL таблицы к которой происходит запрос). + + +**Пример** + +```sql +SELECT * FROM url('jdbc:mysql://localhost:3306/?user=root&password=root', 'schema', 'table') +``` + +```sql +SELECT * FROM url('mysql://localhost:3306/?user=root&password=root', 'schema', 'table') +``` + +```sql +SELECT * FROM url('datasource://mysql-local', 'schema', 'table') +``` \ No newline at end of file From d0981b733dec7fa8a6abcd5bb719e93f4f1036f5 Mon Sep 17 00:00:00 2001 From: VadimPE Date: Mon, 1 Oct 2018 16:27:39 +0300 Subject: [PATCH 20/78] CLICKHOUSE-3947 add recursive search for constant expressions --- .../transformQueryForExternalDatabase.cpp | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/dbms/src/Storages/transformQueryForExternalDatabase.cpp b/dbms/src/Storages/transformQueryForExternalDatabase.cpp index 64e861a0a0b..f502ae1127d 100644 --- a/dbms/src/Storages/transformQueryForExternalDatabase.cpp +++ b/dbms/src/Storages/transformQueryForExternalDatabase.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -13,12 +15,52 @@ namespace DB { +Block getBlockWithConstants( + const ASTPtr & query, const Context & context, const NamesAndTypesList & all_columns) +{ + Block result + { + { DataTypeUInt8().createColumnConstWithDefaultValue(1), std::make_shared(), "_dummy" } + }; + + const auto expr_for_constant_folding = ExpressionAnalyzer{query, context, nullptr, all_columns}.getConstActions(); + + expr_for_constant_folding->execute(result); + + return result; +} + +static void replaceConstFunction(const IAST & node, const Context & context, const NamesAndTypesList & all_columns) +{ + for (size_t i = 0; i < node.children.size(); ++i) { + auto child = node.children[i]; + if (const ASTExpressionList * exp_list = typeid_cast(&*child)) + replaceConstFunction(*exp_list, context, all_columns); + + if (const ASTFunction * function = typeid_cast(&*child)) + { + auto result_block = getBlockWithConstants(function->clone(), context, all_columns); + if (!result_block.has(child->getColumnName())) { + return; + } + auto result_column = result_block.getByName(child->getColumnName()).column; + auto result_string = applyVisitor(FieldVisitorToString(), (*result_column)[0]); + + Field result_field(result_string); + ASTLiteral result_ast(result_field); + + auto new_node = const_cast(&node); + + new_node->children[i] = std::make_shared(result_field); + } + } +} + static bool isCompatible(const IAST & node) { if (const ASTFunction * function = typeid_cast(&node)) { String name = function->name; - if (!(name == "and" || name == "or" || name == "not" @@ -87,6 +129,7 @@ String transformQueryForExternalDatabase( const ASTPtr & original_where = typeid_cast(query).where_expression; if (original_where) { + replaceConstFunction(*original_where, context, available_columns); if (isCompatible(*original_where)) { select->where_expression = original_where; From 0849e1e2188bf1ac64a31736ecb0f229f2307dca Mon Sep 17 00:00:00 2001 From: Vadim Date: Mon, 1 Oct 2018 17:06:36 +0300 Subject: [PATCH 21/78] Update transformQueryForExternalDatabase.cpp --- dbms/src/Storages/transformQueryForExternalDatabase.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dbms/src/Storages/transformQueryForExternalDatabase.cpp b/dbms/src/Storages/transformQueryForExternalDatabase.cpp index f502ae1127d..3224e05f200 100644 --- a/dbms/src/Storages/transformQueryForExternalDatabase.cpp +++ b/dbms/src/Storages/transformQueryForExternalDatabase.cpp @@ -32,15 +32,17 @@ Block getBlockWithConstants( static void replaceConstFunction(const IAST & node, const Context & context, const NamesAndTypesList & all_columns) { - for (size_t i = 0; i < node.children.size(); ++i) { + for (size_t i = 0; i < node.children.size(); ++i) + { auto child = node.children[i]; if (const ASTExpressionList * exp_list = typeid_cast(&*child)) replaceConstFunction(*exp_list, context, all_columns); if (const ASTFunction * function = typeid_cast(&*child)) { - auto result_block = getBlockWithConstants(function->clone(), context, all_columns); - if (!result_block.has(child->getColumnName())) { +_partition_id auto result_block = getBlockWithConstants(function->clone(), context, all_columns); + if (!result_block.has(child->getColumnName())) + { return; } auto result_column = result_block.getByName(child->getColumnName()).column; From 575fa7f25a1bb685cf4509f5317067addbe353d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Vavru=C5=A1a?= Date: Tue, 2 Oct 2018 19:06:04 -0700 Subject: [PATCH 22/78] PushingToViewsBlockOutputStream: add tests for parallel processing This adds tests for a971a0bc07b83af564496879e1a219a4fba9d498 --- ...726_materialized_view_concurrent.reference | 4 ++++ .../00726_materialized_view_concurrent.sql | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00726_materialized_view_concurrent.reference create mode 100644 dbms/tests/queries/0_stateless/00726_materialized_view_concurrent.sql diff --git a/dbms/tests/queries/0_stateless/00726_materialized_view_concurrent.reference b/dbms/tests/queries/0_stateless/00726_materialized_view_concurrent.reference new file mode 100644 index 00000000000..52ee4350de7 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00726_materialized_view_concurrent.reference @@ -0,0 +1,4 @@ +2 +4 +1 +3 diff --git a/dbms/tests/queries/0_stateless/00726_materialized_view_concurrent.sql b/dbms/tests/queries/0_stateless/00726_materialized_view_concurrent.sql new file mode 100644 index 00000000000..1eb47943edd --- /dev/null +++ b/dbms/tests/queries/0_stateless/00726_materialized_view_concurrent.sql @@ -0,0 +1,22 @@ +DROP TABLE IF EXISTS test.src; +DROP TABLE IF EXISTS test.mv1; +DROP TABLE IF EXISTS test.mv2; + +USE test; + +CREATE TABLE src (x UInt8) ENGINE = Null; +CREATE MATERIALIZED VIEW mv1 ENGINE = Memory AS SELECT x FROM src WHERE x % 2 = 0; +CREATE MATERIALIZED VIEW mv2 ENGINE = Memory AS SELECT x FROM src WHERE x % 2 = 1; + +SET parallel_view_processing = 1; +INSERT INTO src VALUES (1), (2); + +SET parallel_view_processing = 0; +INSERT INTO src VALUES (3), (4); + +SELECT * FROM mv1 ORDER BY x; +SELECT * FROM mv2 ORDER BY x; + +DROP TABLE test.mv1; +DROP TABLE test.mv2; +DROP TABLE test.src; \ No newline at end of file From 4152c34fbb7d0a18f44eb4cf9848f42bd7621d43 Mon Sep 17 00:00:00 2001 From: VadimPE Date: Wed, 3 Oct 2018 13:08:02 +0300 Subject: [PATCH 23/78] CLICKHOUSE-4021 fix build --- dbms/src/Storages/transformQueryForExternalDatabase.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dbms/src/Storages/transformQueryForExternalDatabase.cpp b/dbms/src/Storages/transformQueryForExternalDatabase.cpp index 3224e05f200..1cd668d0b64 100644 --- a/dbms/src/Storages/transformQueryForExternalDatabase.cpp +++ b/dbms/src/Storages/transformQueryForExternalDatabase.cpp @@ -40,11 +40,10 @@ static void replaceConstFunction(const IAST & node, const Context & context, con if (const ASTFunction * function = typeid_cast(&*child)) { -_partition_id auto result_block = getBlockWithConstants(function->clone(), context, all_columns); + auto result_block = getBlockWithConstants(function->clone(), context, all_columns); if (!result_block.has(child->getColumnName())) - { return; - } + auto result_column = result_block.getByName(child->getColumnName()).column; auto result_string = applyVisitor(FieldVisitorToString(), (*result_column)[0]); From c7fee9374c9810155f2f28a0808eeb450d4a4981 Mon Sep 17 00:00:00 2001 From: Alexandr Krasheninnikov Date: Wed, 3 Oct 2018 13:44:43 +0300 Subject: [PATCH 24/78] fix formatting errors according code review --- .../odbc-bridge/IdentifierQuoteHandler.cpp | 14 +- .../odbc-bridge/IdentifierQuoteHandler.h | 2 +- .../odbc-bridge/getIdentifierQuote.cpp | 39 ++- dbms/src/Common/XDBCBridgeHelper.h | 84 +++--- dbms/src/Storages/StorageXDBC.cpp | 240 +++++++++--------- .../src/TableFunctions/ITableFunctionXDBC.cpp | 151 +++++------ dbms/src/TableFunctions/ITableFunctionXDBC.h | 25 +- .../ru/query_language/table_functions/jdbc.md | 6 +- 8 files changed, 286 insertions(+), 275 deletions(-) diff --git a/dbms/programs/odbc-bridge/IdentifierQuoteHandler.cpp b/dbms/programs/odbc-bridge/IdentifierQuoteHandler.cpp index 3588ff6c2ca..5dc8b60f091 100644 --- a/dbms/programs/odbc-bridge/IdentifierQuoteHandler.cpp +++ b/dbms/programs/odbc-bridge/IdentifierQuoteHandler.cpp @@ -14,29 +14,27 @@ #define POCO_SQL_ODBC_CLASS Poco::Data::ODBC #endif -#include -#include -#include #include #include #include #include #include +#include +#include +#include #include #include -#include "validateODBCConnectionString.h" #include "getIdentifierQuote.h" +#include "validateODBCConnectionString.h" namespace DB { - void IdentifierQuoteHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) { Poco::Net::HTMLForm params(request, request.stream()); LOG_TRACE(log, "Request URI: " + request.getURI()); - auto process_error = [&response, this](const std::string & message) - { + auto process_error = [&response, this](const std::string & message) { response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); if (!response.sent()) response.send() << message << std::endl; @@ -67,4 +65,4 @@ void IdentifierQuoteHandler::handleRequest(Poco::Net::HTTPServerRequest & reques } } } -#endif +#endif \ No newline at end of file diff --git a/dbms/programs/odbc-bridge/IdentifierQuoteHandler.h b/dbms/programs/odbc-bridge/IdentifierQuoteHandler.h index e7fff8ac001..2d4cf0277be 100644 --- a/dbms/programs/odbc-bridge/IdentifierQuoteHandler.h +++ b/dbms/programs/odbc-bridge/IdentifierQuoteHandler.h @@ -1,8 +1,8 @@ #pragma once -#include #include #include #include +#include #if USE_POCO_SQLODBC || USE_POCO_DATAODBC /** This handler establish connection to database, and retrieve quote style identifier diff --git a/dbms/programs/odbc-bridge/getIdentifierQuote.cpp b/dbms/programs/odbc-bridge/getIdentifierQuote.cpp index 5cfb5abba4b..a8143af4846 100644 --- a/dbms/programs/odbc-bridge/getIdentifierQuote.cpp +++ b/dbms/programs/odbc-bridge/getIdentifierQuote.cpp @@ -17,29 +17,28 @@ namespace DB { - std::string getIdentifierQuote(SQLHDBC hdbc) +std::string getIdentifierQuote(SQLHDBC hdbc) +{ + std::string identifier; + + SQLSMALLINT t; + SQLRETURN r = POCO_SQL_ODBC_CLASS::SQLGetInfo(hdbc, SQL_IDENTIFIER_QUOTE_CHAR, NULL, 0, &t); + + if (POCO_SQL_ODBC_CLASS::Utility::isError(r)) + throw POCO_SQL_ODBC_CLASS::ConnectionException(hdbc); + + if (t > 0) { - std::string identifier = ""; + // I have not idea, why to add '2' here, got from: contrib/poco/Data/ODBC/src/ODBCStatementImpl.cpp:60 (SQL_DRIVER_NAME) + identifier.resize(static_cast(t) + 2); - SQLSMALLINT t; - SQLRETURN r = POCO_SQL_ODBC_CLASS::SQLGetInfo(hdbc, SQL_IDENTIFIER_QUOTE_CHAR, NULL, 0, &t); - - if (POCO_SQL_ODBC_CLASS::Utility::isError(r)) + if (POCO_SQL_ODBC_CLASS::Utility::isError(POCO_SQL_ODBC_CLASS::SQLGetInfo( + hdbc, SQL_IDENTIFIER_QUOTE_CHAR, &identifier[0], SQLSMALLINT((identifier.length() - 1) * sizeof(identifier[0])), &t))) throw POCO_SQL_ODBC_CLASS::ConnectionException(hdbc); - if (t > 0) - { - // I have not idea, why to add '2' here, got from: contrib/poco/Data/ODBC/src/ODBCStatementImpl.cpp:60 (SQL_DRIVER_NAME) - identifier.resize(static_cast(t) + 2); - - if (POCO_SQL_ODBC_CLASS::Utility::isError(POCO_SQL_ODBC_CLASS::SQLGetInfo(hdbc, SQL_IDENTIFIER_QUOTE_CHAR, &identifier[0], SQLSMALLINT((identifier.length() - 1) * sizeof(identifier[0])), &t))) - throw POCO_SQL_ODBC_CLASS::ConnectionException(hdbc); - - identifier.resize(static_cast(t)); - } - return identifier; + identifier.resize(static_cast(t)); } - - + return identifier; } -#endif +} +#endif \ No newline at end of file diff --git a/dbms/src/Common/XDBCBridgeHelper.h b/dbms/src/Common/XDBCBridgeHelper.h index af9033f6d7d..8bb892bf217 100644 --- a/dbms/src/Common/XDBCBridgeHelper.h +++ b/dbms/src/Common/XDBCBridgeHelper.h @@ -1,15 +1,14 @@ #pragma once -#include -#include -#include -#include #include #include #include +#include #include +#include #include #include +#include #include #include #include @@ -18,7 +17,6 @@ namespace DB { - namespace ErrorCodes { extern const int EXTERNAL_EXECUTABLE_NOT_FOUND; @@ -66,7 +64,6 @@ protected: } public: - using Configuration = Poco::Util::AbstractConfiguration; const Configuration & config; @@ -79,9 +76,8 @@ public: static constexpr inline auto IDENTIFIER_QUOTE_HANDLER = "/identifier_quote"; static constexpr inline auto PING_OK_ANSWER = "Ok."; - XDBCBridgeHelper( - const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_) - : http_timeout(http_timeout_), connection_string(connection_string_), config(config_) + XDBCBridgeHelper(const Configuration & config_, const Poco::Timespan & http_timeout_, const std::string & connection_string_) + : http_timeout(http_timeout_), connection_string(connection_string_), config(config_) { size_t bridge_port = config.getUInt(BridgeHelperMixin::configPrefix() + ".port", DEFAULT_PORT); std::string bridge_host = config.getString(BridgeHelperMixin::configPrefix() + ".host", DEFAULT_HOST); @@ -115,9 +111,9 @@ public: if (character.length() == 0) quote_style = IdentifierQuotingStyle::None; - else if(character[0] == '`') + else if (character[0] == '`') quote_style = IdentifierQuotingStyle::Backticks; - else if(character[0] == '"') + else if (character[0] == '"') quote_style = IdentifierQuotingStyle::DoubleQuotes; else throw Exception("Can not map quote identifier '" + character + "' to enum value"); @@ -161,7 +157,8 @@ public: std::this_thread::sleep_for(std::chrono::milliseconds(10)); } if (!started) - throw Exception(BridgeHelperMixin::getName() + "BridgeHelper: " + BridgeHelperMixin::serviceAlias() + " is not responding", ErrorCodes::EXTERNAL_SERVER_IS_NOT_RESPONDING); + throw Exception(BridgeHelperMixin::getName() + "BridgeHelper: " + BridgeHelperMixin::serviceAlias() + " is not responding", + ErrorCodes::EXTERNAL_SERVER_IS_NOT_RESPONDING); } } @@ -188,7 +185,7 @@ public: protected: Poco::URI createBaseURI() const { - size_t bridge_port = config.getUInt(BridgeHelperMixin::serviceAlias()+ ".port", DEFAULT_PORT); + size_t bridge_port = config.getUInt(BridgeHelperMixin::serviceAlias() + ".port", DEFAULT_PORT); std::string bridge_host = config.getString(BridgeHelperMixin::serviceAlias() + ".host", DEFAULT_HOST); Poco::URI uri; @@ -199,8 +196,6 @@ protected: } private: - - bool checkBridgeIsRunning() const { try @@ -224,24 +219,41 @@ private: struct JDBCBridgeMixin { static constexpr inline auto DEFAULT_PORT = 9019; - static const String configPrefix() { return "jdbc_bridge"; } - static const String serviceAlias() { return "clickhouse-jdbc-bridge"; } - static const String getName() { return "JDBC"; } + static const String configPrefix() + { + return "jdbc_bridge"; + } + static const String serviceAlias() + { + return "clickhouse-jdbc-bridge"; + } + static const String getName() + { + return "JDBC"; + } - static void startBridge(const Poco::Util::AbstractConfiguration & , const Poco::Logger * , const Poco::Timespan & ) + static void startBridge(const Poco::Util::AbstractConfiguration &, const Poco::Logger *, const Poco::Timespan &) { throw Exception("jdbc-bridge is not running. Please, start it manually"); } - }; -struct ODBCBridgeMixin { - +struct ODBCBridgeMixin +{ static constexpr inline auto DEFAULT_PORT = 9018; - static const String configPrefix() { return "odbc_bridge"; } - static const String serviceAlias() { return "clickhouse-odbc-bridge"; } - static const String getName() { return "ODBC"; } + static const String configPrefix() + { + return "odbc_bridge"; + } + static const String serviceAlias() + { + return "clickhouse-odbc-bridge"; + } + static const String getName() + { + return "ODBC"; + } static void startBridge(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log, const Poco::Timespan & http_timeout) { @@ -249,9 +261,9 @@ struct ODBCBridgeMixin { path.setFileName( #if CLICKHOUSE_SPLIT_BINARY - "clickhouse-odbc-bridge" + "clickhouse-odbc-bridge" #else - "clickhouse" + "clickhouse" #endif ); @@ -261,18 +273,19 @@ struct ODBCBridgeMixin { std::stringstream command; command << path.toString() << - #if CLICKHOUSE_SPLIT_BINARY - " " - #else - " odbc-bridge " +#if CLICKHOUSE_SPLIT_BINARY + " " +#else + " odbc-bridge " #endif - ; + ; command << "--http-port " << config.getUInt(configPrefix() + ".port", DEFAULT_PORT) << ' '; - command << "--listen-host " << config.getString(configPrefix() + ".listen_host", XDBCBridgeHelper::DEFAULT_HOST) << ' '; + command << "--listen-host " << config.getString(configPrefix() + ".listen_host", XDBCBridgeHelper::DEFAULT_HOST) + << ' '; command << "--http-timeout " << http_timeout.totalMicroseconds() << ' '; - if (config.has("logger." + configPrefix() +"_log")) - command << "--log-path " << config.getString("logger."+configPrefix()+"_log") << ' '; + if (config.has("logger." + configPrefix() + "_log")) + command << "--log-path " << config.getString("logger." + configPrefix() + "_log") << ' '; if (config.has("logger." + configPrefix() + "_errlog")) command << "--err-log-path " << config.getString("logger." + configPrefix() + "_errlog") << ' '; if (config.has("logger." + configPrefix() + "_level")) @@ -289,5 +302,4 @@ struct ODBCBridgeMixin { cmd->wait(); } }; - } \ No newline at end of file diff --git a/dbms/src/Storages/StorageXDBC.cpp b/dbms/src/Storages/StorageXDBC.cpp index 5a3e7623e61..6a2f8a3d6e9 100644 --- a/dbms/src/Storages/StorageXDBC.cpp +++ b/dbms/src/Storages/StorageXDBC.cpp @@ -16,125 +16,123 @@ #include namespace DB { - namespace ErrorCodes - { - extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; - } - - - StorageXDBC::StorageXDBC(const std::string & table_name_, - const std::string & remote_database_name_, - const std::string & remote_table_name_, - const ColumnsDescription & columns_, - const Context & context_, - const BridgeHelperPtr bridge_helper_) - : IStorageURLBase(Poco::URI(), context_, table_name_, IXDBCBridgeHelper::DEFAULT_FORMAT, columns_) - , bridge_helper(bridge_helper_) - , remote_database_name(remote_database_name_) - , remote_table_name(remote_table_name_) - { - log = &Poco::Logger::get("Storage" + bridge_helper->getName()); - uri = bridge_helper->getMainURI(); - } - - std::string StorageXDBC::getReadMethod() const - { - return Poco::Net::HTTPRequest::HTTP_POST; - } - - std::vector> StorageXDBC::getReadURIParams(const Names & column_names, - const SelectQueryInfo & /*query_info*/, - const Context & /*context*/, - QueryProcessingStage::Enum & /*processed_stage*/, - size_t max_block_size) const - { - NamesAndTypesList cols; - for (const String & name : column_names) - { - auto column_data = getColumn(name); - cols.emplace_back(column_data.name, column_data.type); - } - return bridge_helper->getURLParams(cols.toString(), max_block_size); - } - - std::function StorageXDBC::getReadPOSTDataCallback(const Names & /*column_names*/, - const SelectQueryInfo & query_info, - const Context & context, - QueryProcessingStage::Enum & /*processed_stage*/, - size_t /*max_block_size*/) const - { - String query = transformQueryForExternalDatabase( - *query_info.query, getColumns().ordinary, bridge_helper->getIdentifierQuotingStyle(), remote_database_name, remote_table_name, context); - - return [query](std::ostream & os) { os << "query=" << query; }; - } - - BlockInputStreams StorageXDBC::read(const Names & column_names, - const SelectQueryInfo & query_info, - const Context & context, - QueryProcessingStage::Enum processed_stage, - size_t max_block_size, - unsigned num_streams) - { - check(column_names); - - bridge_helper->startBridgeSync(); - return IStorageURLBase::read(column_names, query_info, context, processed_stage, max_block_size, num_streams); - } - - - Block StorageXDBC::getHeaderBlock(const Names & column_names) const - { - return getSampleBlockForColumns(column_names); - } - - std::string StorageXDBC::getName() const - { - return bridge_helper->getName(); - } - - namespace - { - - template - void registerXDBCStorage(StorageFactory & factory, const std::string & name) - { - factory.registerStorage(name, [&name](const StorageFactory::Arguments & args) - { - ASTs & engine_args = args.engine_args; - - if (engine_args.size() != 3) - throw Exception( - "Storage " + name + " requires exactly 3 parameters: " + name + "('DSN', database or schema, table)", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - for (size_t i = 0; i < 3; ++i) - engine_args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[i], args.local_context); - - BridgeHelperPtr bridge_helper = std::make_shared>( - args.context.getConfigRef(), - args.context.getSettingsRef().http_receive_timeout.value, - static_cast(*engine_args[0]).value.safeGet() - ); - return std::make_shared(args.table_name, - static_cast(*engine_args[1]).value.safeGet(), - static_cast(*engine_args[2]).value.safeGet(), - args.columns, - args.context, - bridge_helper - ); - - }); - } - } - - void registerStorageJDBC(StorageFactory & factory) - { - registerXDBCStorage(factory, "JDBC"); - } - - void registerStorageODBC(StorageFactory & factory) - { - registerXDBCStorage(factory, "ODBC"); - } - +namespace ErrorCodes +{ + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } + + +StorageXDBC::StorageXDBC(const std::string & table_name_, + const std::string & remote_database_name_, + const std::string & remote_table_name_, + const ColumnsDescription & columns_, + const Context & context_, + const BridgeHelperPtr bridge_helper_) + : IStorageURLBase(Poco::URI(), context_, table_name_, IXDBCBridgeHelper::DEFAULT_FORMAT, columns_) + , bridge_helper(bridge_helper_) + , remote_database_name(remote_database_name_) + , remote_table_name(remote_table_name_) +{ + log = &Poco::Logger::get("Storage" + bridge_helper->getName()); + uri = bridge_helper->getMainURI(); +} + +std::string StorageXDBC::getReadMethod() const +{ + return Poco::Net::HTTPRequest::HTTP_POST; +} + +std::vector> StorageXDBC::getReadURIParams(const Names & column_names, + const SelectQueryInfo & /*query_info*/, + const Context & /*context*/, + QueryProcessingStage::Enum & /*processed_stage*/, + size_t max_block_size) const +{ + NamesAndTypesList cols; + for (const String & name : column_names) + { + auto column_data = getColumn(name); + cols.emplace_back(column_data.name, column_data.type); + } + return bridge_helper->getURLParams(cols.toString(), max_block_size); +} + +std::function StorageXDBC::getReadPOSTDataCallback(const Names & /*column_names*/, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum & /*processed_stage*/, + size_t /*max_block_size*/) const +{ + String query = transformQueryForExternalDatabase(*query_info.query, + getColumns().ordinary, + bridge_helper->getIdentifierQuotingStyle(), + remote_database_name, + remote_table_name, + context); + + return [query](std::ostream & os) { os << "query=" << query; }; +} + +BlockInputStreams StorageXDBC::read(const Names & column_names, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum processed_stage, + size_t max_block_size, + unsigned num_streams) +{ + check(column_names); + + bridge_helper->startBridgeSync(); + return IStorageURLBase::read(column_names, query_info, context, processed_stage, max_block_size, num_streams); +} + + +Block StorageXDBC::getHeaderBlock(const Names & column_names) const +{ + return getSampleBlockForColumns(column_names); +} + +std::string StorageXDBC::getName() const +{ + return bridge_helper->getName(); +} + +namespace +{ + template + void registerXDBCStorage(StorageFactory & factory, const std::string & name) + { + factory.registerStorage(name, [&name](const StorageFactory::Arguments & args) { + ASTs & engine_args = args.engine_args; + + if (engine_args.size() != 3) + throw Exception("Storage " + name + " requires exactly 3 parameters: " + name + "('DSN', database or schema, table)", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + for (size_t i = 0; i < 3; ++i) + engine_args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[i], args.local_context); + + BridgeHelperPtr bridge_helper = std::make_shared>(args.context.getConfigRef(), + args.context.getSettingsRef().http_receive_timeout.value, + static_cast(*engine_args[0]).value.safeGet()); + return std::make_shared(args.table_name, + static_cast(*engine_args[1]).value.safeGet(), + static_cast(*engine_args[2]).value.safeGet(), + args.columns, + args.context, + bridge_helper); + + }); + } +} + +void registerStorageJDBC(StorageFactory & factory) +{ + registerXDBCStorage(factory, "JDBC"); +} + +void registerStorageODBC(StorageFactory & factory) +{ + registerXDBCStorage(factory, "ODBC"); +} +} \ No newline at end of file diff --git a/dbms/src/TableFunctions/ITableFunctionXDBC.cpp b/dbms/src/TableFunctions/ITableFunctionXDBC.cpp index 482cc605810..1fe73441b85 100644 --- a/dbms/src/TableFunctions/ITableFunctionXDBC.cpp +++ b/dbms/src/TableFunctions/ITableFunctionXDBC.cpp @@ -1,94 +1,97 @@ #include #include +#include #include -#include #include #include +#include #include #include #include #include -#include +#include #include #include #include -#include +#include #include #include -#include namespace DB { - namespace ErrorCodes - { - extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; - } - - StoragePtr ITableFunctionXDBC::executeImpl(const ASTPtr & ast_function, const Context & context) const - { - const ASTFunction & args_func = typeid_cast(*ast_function); - - if (!args_func.arguments) - throw Exception("Table function '" + getName() + "' must have arguments.", ErrorCodes::LOGICAL_ERROR); - - ASTs & args = typeid_cast(*args_func.arguments).children; - if (args.size() != 2 && args.size() != 3) - throw Exception("Table function '" + getName() + "' requires 2 or 3 arguments: " + getName() + "('DSN', table) or "+getName()+"('DSN', schema, table)", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - for (auto i = 0u; i < args.size(); ++i) - args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(args[i], context); - - std::string connection_string = ""; - std::string schema_name = ""; - std::string table_name = ""; - if (args.size() == 3) - { - connection_string = static_cast(*args[0]).value.safeGet(); - schema_name = static_cast(*args[1]).value.safeGet(); - table_name = static_cast(*args[2]).value.safeGet(); - } else if (args.size() == 2) - { - connection_string = static_cast(*args[0]).value.safeGet(); - table_name = static_cast(*args[1]).value.safeGet(); - } - - const auto & config = context.getConfigRef(); - - /* Infer external table structure */ - BridgeHelperPtr helper = createBridgeHelper(config, context.getSettingsRef().http_receive_timeout.value, connection_string); - helper->startBridgeSync(); - - Poco::URI columns_info_uri = helper->getColumnsInfoURI(); - columns_info_uri.addQueryParameter("connection_string", connection_string); - if (!schema_name.empty()) - columns_info_uri.addQueryParameter("schema", schema_name); - columns_info_uri.addQueryParameter("table", table_name); - - ReadWriteBufferFromHTTP buf(columns_info_uri, Poco::Net::HTTPRequest::HTTP_POST, nullptr); - - std::string columns_info; - readStringBinary(columns_info, buf); - NamesAndTypesList columns = NamesAndTypesList::parse(columns_info); - - auto result = std::make_shared(table_name, schema_name, table_name, ColumnsDescription{columns}, context, helper); - - if(!result) - throw Exception("Failed to instantiate storage from table function " + getName()); - - result->startup(); - return result; - } - - void registerTableFunctionJDBC(TableFunctionFactory & factory) - { - factory.registerFunction(); - } - - void registerTableFunctionODBC(TableFunctionFactory & factory) - { - factory.registerFunction(); - } +namespace ErrorCodes +{ + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } + +StoragePtr ITableFunctionXDBC::executeImpl(const ASTPtr & ast_function, const Context & context) const +{ + const ASTFunction & args_func = typeid_cast(*ast_function); + + if (!args_func.arguments) + throw Exception("Table function '" + getName() + "' must have arguments.", ErrorCodes::LOGICAL_ERROR); + + ASTs & args = typeid_cast(*args_func.arguments).children; + if (args.size() != 2 && args.size() != 3) + throw Exception("Table function '" + getName() + "' requires 2 or 3 arguments: " + getName() + "('DSN', table) or " + getName() + + "('DSN', schema, table)", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + for (auto i = 0u; i < args.size(); ++i) + args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(args[i], context); + + std::string connection_string = ""; + std::string schema_name = ""; + std::string table_name = ""; + + if (args.size() == 3) + { + connection_string = static_cast(*args[0]).value.safeGet(); + schema_name = static_cast(*args[1]).value.safeGet(); + table_name = static_cast(*args[2]).value.safeGet(); + } + else if (args.size() == 2) + { + connection_string = static_cast(*args[0]).value.safeGet(); + table_name = static_cast(*args[1]).value.safeGet(); + } + + const auto & config = context.getConfigRef(); + + /* Infer external table structure */ + BridgeHelperPtr helper = createBridgeHelper(config, context.getSettingsRef().http_receive_timeout.value, connection_string); + helper->startBridgeSync(); + + Poco::URI columns_info_uri = helper->getColumnsInfoURI(); + columns_info_uri.addQueryParameter("connection_string", connection_string); + if (!schema_name.empty()) + columns_info_uri.addQueryParameter("schema", schema_name); + columns_info_uri.addQueryParameter("table", table_name); + + ReadWriteBufferFromHTTP buf(columns_info_uri, Poco::Net::HTTPRequest::HTTP_POST, nullptr); + + std::string columns_info; + readStringBinary(columns_info, buf); + NamesAndTypesList columns = NamesAndTypesList::parse(columns_info); + + auto result = std::make_shared(table_name, schema_name, table_name, ColumnsDescription{columns}, context, helper); + + if (!result) + throw Exception("Failed to instantiate storage from table function " + getName()); + + result->startup(); + return result; +} + +void registerTableFunctionJDBC(TableFunctionFactory & factory) +{ + factory.registerFunction(); +} + +void registerTableFunctionODBC(TableFunctionFactory & factory) +{ + factory.registerFunction(); +} +} \ No newline at end of file diff --git a/dbms/src/TableFunctions/ITableFunctionXDBC.h b/dbms/src/TableFunctions/ITableFunctionXDBC.h index 055d4db022b..3a753457f55 100644 --- a/dbms/src/TableFunctions/ITableFunctionXDBC.h +++ b/dbms/src/TableFunctions/ITableFunctionXDBC.h @@ -1,10 +1,10 @@ #pragma once -#include -#include +#include #include #include -#include +#include +#include namespace DB { @@ -19,8 +19,8 @@ private: /* A factory method to create bridge helper, that will assist in remote interaction */ virtual BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, - const Poco::Timespan & http_timeout_, - const std::string & connection_string_) const = 0; + const Poco::Timespan & http_timeout_, + const std::string & connection_string_) const = 0; }; class TableFunctionJDBC : public ITableFunctionXDBC @@ -31,11 +31,12 @@ public: { return name; } -private: +private: BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, - const Poco::Timespan & http_timeout_, - const std::string & connection_string_) const override { + const Poco::Timespan & http_timeout_, + const std::string & connection_string_) const override + { return std::make_shared>(config_, http_timeout_, connection_string_); } }; @@ -48,13 +49,13 @@ public: { return name; } -private: +private: BridgeHelperPtr createBridgeHelper(const Poco::Util::AbstractConfiguration & config_, - const Poco::Timespan & http_timeout_, - const std::string & connection_string_) const override { + const Poco::Timespan & http_timeout_, + const std::string & connection_string_) const override + { return std::make_shared>(config_, http_timeout_, connection_string_); } }; - } diff --git a/docs/ru/query_language/table_functions/jdbc.md b/docs/ru/query_language/table_functions/jdbc.md index 1d8721e25a5..8a7ca2c4acd 100644 --- a/docs/ru/query_language/table_functions/jdbc.md +++ b/docs/ru/query_language/table_functions/jdbc.md @@ -11,13 +11,13 @@ **Пример** ```sql -SELECT * FROM url('jdbc:mysql://localhost:3306/?user=root&password=root', 'schema', 'table') +SELECT * FROM jdbc('jdbc:mysql://localhost:3306/?user=root&password=root', 'schema', 'table') ``` ```sql -SELECT * FROM url('mysql://localhost:3306/?user=root&password=root', 'schema', 'table') +SELECT * FROM jdbc('mysql://localhost:3306/?user=root&password=root', 'schema', 'table') ``` ```sql -SELECT * FROM url('datasource://mysql-local', 'schema', 'table') +SELECT * FROM jdbc('datasource://mysql-local', 'schema', 'table') ``` \ No newline at end of file From 8df97e32af99e50bd6b15420b8a3b1a2c5133e74 Mon Sep 17 00:00:00 2001 From: VadimPE Date: Wed, 3 Oct 2018 15:03:39 +0300 Subject: [PATCH 25/78] CLICKHOUSE-3947 fix bug with query.clone() --- .../src/Storages/transformQueryForExternalDatabase.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dbms/src/Storages/transformQueryForExternalDatabase.cpp b/dbms/src/Storages/transformQueryForExternalDatabase.cpp index 1cd668d0b64..ad04751c1bd 100644 --- a/dbms/src/Storages/transformQueryForExternalDatabase.cpp +++ b/dbms/src/Storages/transformQueryForExternalDatabase.cpp @@ -47,12 +47,9 @@ static void replaceConstFunction(const IAST & node, const Context & context, con auto result_column = result_block.getByName(child->getColumnName()).column; auto result_string = applyVisitor(FieldVisitorToString(), (*result_column)[0]); - Field result_field(result_string); - ASTLiteral result_ast(result_field); - auto new_node = const_cast(&node); - new_node->children[i] = std::make_shared(result_field); + new_node->children[i] = std::make_shared((*result_column)[0]); } } } @@ -108,7 +105,8 @@ String transformQueryForExternalDatabase( const String & table, const Context & context) { - ExpressionAnalyzer analyzer(query.clone(), context, {}, available_columns); + auto clone_query = query.clone(); + ExpressionAnalyzer analyzer(clone_query, context, {}, available_columns); const Names & used_columns = analyzer.getRequiredSourceColumns(); auto select = std::make_shared(); @@ -127,7 +125,7 @@ String transformQueryForExternalDatabase( * copy only compatible parts of it. */ - const ASTPtr & original_where = typeid_cast(query).where_expression; + const ASTPtr & original_where = typeid_cast(*clone_query).where_expression; if (original_where) { replaceConstFunction(*original_where, context, available_columns); From 40350c47e157a046b426a79e6a4f6e79342472b1 Mon Sep 17 00:00:00 2001 From: VadimPE Date: Wed, 3 Oct 2018 15:10:35 +0300 Subject: [PATCH 26/78] CLICKHOUSE-3947 del unused result_string --- dbms/src/Storages/transformQueryForExternalDatabase.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/dbms/src/Storages/transformQueryForExternalDatabase.cpp b/dbms/src/Storages/transformQueryForExternalDatabase.cpp index ad04751c1bd..bac18b71b82 100644 --- a/dbms/src/Storages/transformQueryForExternalDatabase.cpp +++ b/dbms/src/Storages/transformQueryForExternalDatabase.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -45,7 +44,6 @@ static void replaceConstFunction(const IAST & node, const Context & context, con return; auto result_column = result_block.getByName(child->getColumnName()).column; - auto result_string = applyVisitor(FieldVisitorToString(), (*result_column)[0]); auto new_node = const_cast(&node); From 82200bc16afc00db999772d929fe8988984250de Mon Sep 17 00:00:00 2001 From: Alexandr Krasheninnikov Date: Wed, 3 Oct 2018 15:10:57 +0300 Subject: [PATCH 27/78] Added error codes, bridge startup, fixed destructor in XDBC stuff --- .../odbc-bridge/ColumnInfoHandler.cpp | 9 +- dbms/src/Common/XDBCBridgeHelper.h | 14 +-- .../src/Dictionaries/XDBCDictionarySource.cpp | 28 ++--- dbms/src/Dictionaries/XDBCDictionarySource.h | 102 +++++++++--------- .../src/TableFunctions/ITableFunctionXDBC.cpp | 10 +- 5 files changed, 85 insertions(+), 78 deletions(-) diff --git a/dbms/programs/odbc-bridge/ColumnInfoHandler.cpp b/dbms/programs/odbc-bridge/ColumnInfoHandler.cpp index d5c7f7e0105..7c345b8b263 100644 --- a/dbms/programs/odbc-bridge/ColumnInfoHandler.cpp +++ b/dbms/programs/odbc-bridge/ColumnInfoHandler.cpp @@ -59,6 +59,11 @@ namespace } } +namespace ErrorCodes +{ + extern const int ILLEGAL_TYPE_OF_ARGUMENT; +} + void ODBCColumnsInfoHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) { Poco::Net::HTMLForm params(request, request.stream()); @@ -123,12 +128,12 @@ void ODBCColumnsInfoHandler::handleRequest(Poco::Net::HTTPServerRequest & reques else if(identifier_quote[0] == '"') settings.identifier_quoting_style = IdentifierQuotingStyle::DoubleQuotes; else - throw Exception("Can not map quote identifier '" + identifier_quote + "' to IdentifierQuotingStyle value"); + throw Exception("Can not map quote identifier '" + identifier_quote + "' to IdentifierQuotingStyle value", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); select->format(settings); std::string query = ss.str(); - std::cout << query << std::endl; + LOG_TRACE(log, "Inferring structure with query '" << query << "'"); if (POCO_SQL_ODBC_CLASS::Utility::isError(POCO_SQL_ODBC_CLASS::SQLPrepare(hstmt, reinterpret_cast(query.data()), query.size()))) throw POCO_SQL_ODBC_CLASS::DescriptorException(session.dbc()); diff --git a/dbms/src/Common/XDBCBridgeHelper.h b/dbms/src/Common/XDBCBridgeHelper.h index 8bb892bf217..67e9fda7af5 100644 --- a/dbms/src/Common/XDBCBridgeHelper.h +++ b/dbms/src/Common/XDBCBridgeHelper.h @@ -21,6 +21,7 @@ namespace ErrorCodes { extern const int EXTERNAL_EXECUTABLE_NOT_FOUND; extern const int EXTERNAL_SERVER_IS_NOT_RESPONDING; + extern const int ILLEGAL_TYPE_OF_ARGUMENT; } /** @@ -38,7 +39,7 @@ public: virtual IdentifierQuotingStyle getIdentifierQuotingStyle() = 0; virtual String getName() const = 0; - virtual ~IXDBCBridgeHelper() {} + virtual ~IXDBCBridgeHelper() = default; }; using BridgeHelperPtr = std::shared_ptr; @@ -99,6 +100,8 @@ public: { if (!quote_style.has_value()) { + startBridgeSync(); + auto uri = createBaseURI(); uri.setPath(IDENTIFIER_QUOTE_HANDLER); uri.addQueryParameter("connection_string", getConnectionString()); @@ -107,16 +110,15 @@ public: std::string character; readStringBinary(character, buf); if (character.length() > 1) - throw Exception("Failed to get quoting style from " + BridgeHelperMixin::serviceAlias()); - - if (character.length() == 0) + throw Exception("Failed to parse quoting style from '" + character + "' for service " + BridgeHelperMixin::serviceAlias(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + else if (character.length() == 0) quote_style = IdentifierQuotingStyle::None; else if (character[0] == '`') quote_style = IdentifierQuotingStyle::Backticks; else if (character[0] == '"') quote_style = IdentifierQuotingStyle::DoubleQuotes; else - throw Exception("Can not map quote identifier '" + character + "' to enum value"); + throw Exception("Can not map quote identifier '" + character + "' to enum value", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); } return *quote_style; @@ -234,7 +236,7 @@ struct JDBCBridgeMixin static void startBridge(const Poco::Util::AbstractConfiguration &, const Poco::Logger *, const Poco::Timespan &) { - throw Exception("jdbc-bridge is not running. Please, start it manually"); + throw Exception("jdbc-bridge is not running. Please, start it manually", ErrorCodes::EXTERNAL_SERVER_IS_NOT_RESPONDING); } }; diff --git a/dbms/src/Dictionaries/XDBCDictionarySource.cpp b/dbms/src/Dictionaries/XDBCDictionarySource.cpp index 0fb0a938551..4e9637502e7 100644 --- a/dbms/src/Dictionaries/XDBCDictionarySource.cpp +++ b/dbms/src/Dictionaries/XDBCDictionarySource.cpp @@ -60,26 +60,26 @@ static const size_t max_block_size = 8192; XDBCDictionarySource::XDBCDictionarySource(const DictionaryStructure & dict_struct_, - const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix, - const Block & sample_block, const Context & context, const BridgeHelperPtr bridge) - : log(&Logger::get(bridge->getName() + "DictionarySource")), + const Poco::Util::AbstractConfiguration & config_, const std::string & config_prefix_, + const Block & sample_block_, const Context & context_, const BridgeHelperPtr bridge_) + : log(&Logger::get(bridge_->getName() + "DictionarySource")), update_time{std::chrono::system_clock::from_time_t(0)}, dict_struct{dict_struct_}, - db{config.getString(config_prefix + ".db", "")}, - table{config.getString(config_prefix + ".table")}, - where{config.getString(config_prefix + ".where", "")}, - update_field{config.getString(config_prefix + ".update_field", "")}, - sample_block{sample_block}, - query_builder{dict_struct, db, table, where, bridge->getIdentifierQuotingStyle()}, + db{config_.getString(config_prefix_ + ".db", "")}, + table{config_.getString(config_prefix_ + ".table")}, + where{config_.getString(config_prefix_ + ".where", "")}, + update_field{config_.getString(config_prefix_ + ".update_field", "")}, + sample_block{sample_block_}, + query_builder{dict_struct, db, table, where, bridge_->getIdentifierQuotingStyle()}, load_all_query{query_builder.composeLoadAllQuery()}, - invalidate_query{config.getString(config_prefix + ".invalidate_query", "")}, - bridge_helper{bridge}, - timeouts{ConnectionTimeouts::getHTTPTimeouts(context.getSettingsRef())}, - global_context(context) + invalidate_query{config_.getString(config_prefix_ + ".invalidate_query", "")}, + bridge_helper{bridge_}, + timeouts{ConnectionTimeouts::getHTTPTimeouts(context_.getSettingsRef())}, + global_context(context_) { bridge_url = bridge_helper->getMainURI(); - auto url_params = bridge_helper->getURLParams(sample_block.getNamesAndTypesList().toString(), max_block_size); + auto url_params = bridge_helper->getURLParams(sample_block_.getNamesAndTypesList().toString(), max_block_size); for (const auto & [name, value] : url_params) bridge_url.addQueryParameter(name, value); } diff --git a/dbms/src/Dictionaries/XDBCDictionarySource.h b/dbms/src/Dictionaries/XDBCDictionarySource.h index b3c7bbc9d70..352b7eecbd3 100644 --- a/dbms/src/Dictionaries/XDBCDictionarySource.h +++ b/dbms/src/Dictionaries/XDBCDictionarySource.h @@ -3,86 +3,84 @@ #include #include -#include -#include #include +#include +#include -#include #include +#include namespace Poco { - namespace Util - { - class AbstractConfiguration; - } +namespace Util +{ + class AbstractConfiguration; +} - class Logger; +class Logger; } namespace DB { - /// Allows loading dictionaries from a XDBC source via bridges - class XDBCDictionarySource final : public IDictionarySource - { - public: - XDBCDictionarySource(const DictionaryStructure & dict_struct_, - const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix, - const Block & sample_block, const Context & context, BridgeHelperPtr bridge); +class XDBCDictionarySource final : public IDictionarySource +{ +public: + XDBCDictionarySource(const DictionaryStructure & dict_struct_, + const Poco::Util::AbstractConfiguration & config_, + const std::string & config_prefix_, + const Block & sample_block_, + const Context & context_, + BridgeHelperPtr bridge); - /// copy-constructor is provided in order to support cloneability - XDBCDictionarySource(const XDBCDictionarySource & other); + /// copy-constructor is provided in order to support cloneability + XDBCDictionarySource(const XDBCDictionarySource & other); - BlockInputStreamPtr loadAll() override; + BlockInputStreamPtr loadAll() override; - BlockInputStreamPtr loadUpdatedAll() override; + BlockInputStreamPtr loadUpdatedAll() override; - BlockInputStreamPtr loadIds(const std::vector & ids) override; + BlockInputStreamPtr loadIds(const std::vector & ids) override; - BlockInputStreamPtr loadKeys( - const Columns & key_columns, const std::vector & requested_rows) override; + BlockInputStreamPtr loadKeys(const Columns & key_columns, const std::vector & requested_rows) override; - bool isModified() const override; + bool isModified() const override; - bool supportsSelectiveLoad() const override; + bool supportsSelectiveLoad() const override; - bool hasUpdateField() const override; + bool hasUpdateField() const override; - DictionarySourcePtr clone() const override; + DictionarySourcePtr clone() const override; - std::string toString() const override; + std::string toString() const override; - private: - std::string getUpdateFieldAndDate(); +private: + std::string getUpdateFieldAndDate(); - // execute invalidate_query. expects single cell in result - std::string doInvalidateQuery(const std::string & request) const; + // execute invalidate_query. expects single cell in result + std::string doInvalidateQuery(const std::string & request) const; - BlockInputStreamPtr loadBase(const std::string & query) const; + BlockInputStreamPtr loadBase(const std::string & query) const; - Poco::Logger * log; - - std::chrono::time_point update_time; - const DictionaryStructure dict_struct; - const std::string db; - const std::string table; - const std::string where; - const std::string update_field; - Block sample_block; - ExternalQueryBuilder query_builder; - const std::string load_all_query; - std::string invalidate_query; - mutable std::string invalidate_query_response; - - BridgeHelperPtr bridge_helper; - Poco::URI bridge_url; - ConnectionTimeouts timeouts; - const Context & global_context; - - }; + Poco::Logger * log; + std::chrono::time_point update_time; + const DictionaryStructure dict_struct; + const std::string db; + const std::string table; + const std::string where; + const std::string update_field; + Block sample_block; + ExternalQueryBuilder query_builder; + const std::string load_all_query; + std::string invalidate_query; + mutable std::string invalidate_query_response; + BridgeHelperPtr bridge_helper; + Poco::URI bridge_url; + ConnectionTimeouts timeouts; + const Context & global_context; +}; } diff --git a/dbms/src/TableFunctions/ITableFunctionXDBC.cpp b/dbms/src/TableFunctions/ITableFunctionXDBC.cpp index 1fe73441b85..7b5948961b5 100644 --- a/dbms/src/TableFunctions/ITableFunctionXDBC.cpp +++ b/dbms/src/TableFunctions/ITableFunctionXDBC.cpp @@ -24,6 +24,8 @@ namespace DB namespace ErrorCodes { extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; + extern const int UNKNOWN_EXCEPTION; + extern const int LOGICAL_ERROR; } StoragePtr ITableFunctionXDBC::executeImpl(const ASTPtr & ast_function, const Context & context) const @@ -42,9 +44,9 @@ StoragePtr ITableFunctionXDBC::executeImpl(const ASTPtr & ast_function, const Co for (auto i = 0u; i < args.size(); ++i) args[i] = evaluateConstantExpressionOrIdentifierAsLiteral(args[i], context); - std::string connection_string = ""; - std::string schema_name = ""; - std::string table_name = ""; + std::string connection_string; + std::string schema_name; + std::string table_name; if (args.size() == 3) { @@ -79,7 +81,7 @@ StoragePtr ITableFunctionXDBC::executeImpl(const ASTPtr & ast_function, const Co auto result = std::make_shared(table_name, schema_name, table_name, ColumnsDescription{columns}, context, helper); if (!result) - throw Exception("Failed to instantiate storage from table function " + getName()); + throw Exception("Failed to instantiate storage from table function " + getName(), ErrorCodes::UNKNOWN_EXCEPTION); result->startup(); return result; From d94b574644e9bba968d33cfd164622515262f5a3 Mon Sep 17 00:00:00 2001 From: Alexandr Krasheninnikov Date: Thu, 4 Oct 2018 16:19:08 +0300 Subject: [PATCH 28/78] Removing destructor --- dbms/src/Common/XDBCBridgeHelper.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dbms/src/Common/XDBCBridgeHelper.h b/dbms/src/Common/XDBCBridgeHelper.h index 67e9fda7af5..df7b72b6c72 100644 --- a/dbms/src/Common/XDBCBridgeHelper.h +++ b/dbms/src/Common/XDBCBridgeHelper.h @@ -88,9 +88,7 @@ public: ping_url.setScheme("http"); ping_url.setPath(PING_HANDLER); } - - virtual ~XDBCBridgeHelper() {} - + String getName() const override { return BridgeHelperMixin::getName(); From cf6f5b314c5bedaedc03f6b82003b2fa175d7882 Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Thu, 4 Oct 2018 22:40:10 +0300 Subject: [PATCH 29/78] Update 00726_materialized_view_concurrent.sql --- .../queries/0_stateless/00726_materialized_view_concurrent.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/tests/queries/0_stateless/00726_materialized_view_concurrent.sql b/dbms/tests/queries/0_stateless/00726_materialized_view_concurrent.sql index 1eb47943edd..53618d6e2ba 100644 --- a/dbms/tests/queries/0_stateless/00726_materialized_view_concurrent.sql +++ b/dbms/tests/queries/0_stateless/00726_materialized_view_concurrent.sql @@ -19,4 +19,4 @@ SELECT * FROM mv2 ORDER BY x; DROP TABLE test.mv1; DROP TABLE test.mv2; -DROP TABLE test.src; \ No newline at end of file +DROP TABLE test.src; From 63c309bf3e3b2ec5d26ab9e14b14afd69f5c69d4 Mon Sep 17 00:00:00 2001 From: BayoNet Date: Fri, 5 Oct 2018 18:19:08 +0300 Subject: [PATCH 30/78] Added the warning about the vulnerability of the ODBC driver. --- .../dicts/external_dicts_dict_sources.md | 29 ++++++++++++++++ .../dicts/external_dicts_dict_sources.md | 34 +++++++++++++++++-- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/docs/en/query_language/dicts/external_dicts_dict_sources.md b/docs/en/query_language/dicts/external_dicts_dict_sources.md index 5614eda030a..e5803b3ac02 100644 --- a/docs/en/query_language/dicts/external_dicts_dict_sources.md +++ b/docs/en/query_language/dicts/external_dicts_dict_sources.md @@ -124,6 +124,35 @@ Setting fields: - `connection_string` – Connection string. - `invalidate_query` – Query for checking the dictionary status. Optional parameter. Read more in the section [Updating dictionaries](external_dicts_dict_lifetime.md#dicts-external_dicts_dict_lifetime). + +### The vulnerability of the ODBC driver + +!!!attention + If when connecting to the database through the ODBC driver you substitute connection parameter `Servername` then values of `USERNAME` and `PASSWORD` from `odbc.ini` are sent to the remote server and can be compromised. + +**Example of insecure use** + +Let's configure unixODBC for PostgreSQL. Content of `/etc/odbc.ini` is: + +``` +[gregtest] +Driver = /usr/lib/psqlodbca.so +Servername = localhost +PORT = 5432 +DATABASE = test_db +#OPTION = 3 +USERNAME = test +PASSWORD = test +``` + +If then you make a query such as + +``` +SELECT * FROM odbc('DSN=gregtest;Servername=some-server.com', 'test_db'); +``` + +then ODBC driver sends values of `USERNAME` and `PASSWORD` for `Servername = localhost` to `some-server.com`. + ### Example of Connecting PostgreSQL Ubuntu OS. diff --git a/docs/ru/query_language/dicts/external_dicts_dict_sources.md b/docs/ru/query_language/dicts/external_dicts_dict_sources.md index 01c94e560fe..37b68d879d8 100644 --- a/docs/ru/query_language/dicts/external_dicts_dict_sources.md +++ b/docs/ru/query_language/dicts/external_dicts_dict_sources.md @@ -124,6 +124,34 @@ - `connection_string` - строка соединения. - `invalidate_query` - запрос для проверки статуса словаря. Необязательный параметр. Читайте подробнее в разделе [Обновление словарей](external_dicts_dict_lifetime.md#dicts-external_dicts_dict_lifetime). +### The vulnerability of the ODBC driver + +!!!attention + Если при соединении с базой данных через ODBC в параметрах соединения заменить `Servername`, то значения `USERNAME` и `PASSWORD` из `odbc.ini` будут отправлены на удаленный сервер и могут быть скомпроментированы. + +**Пример небезопасного использования** + +Сконфигурируем unixODBC для работы с PostgreSQL. Содержимое `/etc/odbc.ini`: + +``` +[gregtest] +Driver = /usr/lib/psqlodbca.so +Servername = localhost +PORT = 5432 +DATABASE = test_db +#OPTION = 3 +USERNAME = test +PASSWORD = test +``` + +Если выполнить запрос вида: + +``` +SELECT * FROM odbc('DSN=gregtest;Servername=some-server.com', 'test_db'); +``` + +то ODBC драйвер отправит значения `USERNAME` и `PASSWORD` для `Servername = localhost` на `some-server.com`. + ### Пример подключения PostgreSQL ОС Ubuntu. @@ -203,7 +231,7 @@ Настройка драйвера: : ``` - $ cat /etc/freetds/freetds.conf + $ cat /etc/freetds/freetds.conf ... [MSSQL] @@ -212,7 +240,7 @@ tds version = 7.0 client charset = UTF-8 - $ cat /etc/odbcinst.ini + $ cat /etc/odbcinst.ini ... [FreeTDS] @@ -222,7 +250,7 @@ FileUsage = 1 UsageCount = 5 - $ cat ~/.odbc.ini + $ cat ~/.odbc.ini ... [MSSQL] From a6d95b9460c9726561fb1d56e1289dbaac0fd008 Mon Sep 17 00:00:00 2001 From: BayoNet Date: Mon, 8 Oct 2018 13:20:34 +0300 Subject: [PATCH 31/78] Textual edits in ODBC dictionary description. --- .../dicts/external_dicts_dict_sources.md | 12 ++++++------ .../dicts/external_dicts_dict_sources.md | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/en/query_language/dicts/external_dicts_dict_sources.md b/docs/en/query_language/dicts/external_dicts_dict_sources.md index e5803b3ac02..4c30852c770 100644 --- a/docs/en/query_language/dicts/external_dicts_dict_sources.md +++ b/docs/en/query_language/dicts/external_dicts_dict_sources.md @@ -125,14 +125,14 @@ Setting fields: - `invalidate_query` – Query for checking the dictionary status. Optional parameter. Read more in the section [Updating dictionaries](external_dicts_dict_lifetime.md#dicts-external_dicts_dict_lifetime). -### The vulnerability of the ODBC driver +### Known vulnerability of the ODBC dictionary functionality -!!!attention - If when connecting to the database through the ODBC driver you substitute connection parameter `Servername` then values of `USERNAME` and `PASSWORD` from `odbc.ini` are sent to the remote server and can be compromised. +!!! attention + When connecting to the database through the ODBC driver connection parameter `Servername` can be substituted. In this case values of `USERNAME` and `PASSWORD` from `odbc.ini` are sent to the remote server and can be compromised. **Example of insecure use** -Let's configure unixODBC for PostgreSQL. Content of `/etc/odbc.ini` is: +Let's configure unixODBC for PostgreSQL. Content of `/etc/odbc.ini`: ``` [gregtest] @@ -145,13 +145,13 @@ USERNAME = test PASSWORD = test ``` -If then you make a query such as +If you then make a query such as ``` SELECT * FROM odbc('DSN=gregtest;Servername=some-server.com', 'test_db'); ``` -then ODBC driver sends values of `USERNAME` and `PASSWORD` for `Servername = localhost` to `some-server.com`. +ODBC driver will send values of `USERNAME` and `PASSWORD` from `odbc.ini` to `some-server.com`. ### Example of Connecting PostgreSQL diff --git a/docs/ru/query_language/dicts/external_dicts_dict_sources.md b/docs/ru/query_language/dicts/external_dicts_dict_sources.md index 37b68d879d8..6b2c455b5bc 100644 --- a/docs/ru/query_language/dicts/external_dicts_dict_sources.md +++ b/docs/ru/query_language/dicts/external_dicts_dict_sources.md @@ -124,10 +124,10 @@ - `connection_string` - строка соединения. - `invalidate_query` - запрос для проверки статуса словаря. Необязательный параметр. Читайте подробнее в разделе [Обновление словарей](external_dicts_dict_lifetime.md#dicts-external_dicts_dict_lifetime). -### The vulnerability of the ODBC driver +### Выявленная уязвимость в функционировании ODBC словарей -!!!attention - Если при соединении с базой данных через ODBC в параметрах соединения заменить `Servername`, то значения `USERNAME` и `PASSWORD` из `odbc.ini` будут отправлены на удаленный сервер и могут быть скомпроментированы. +!!! attention + При соединении с базой данных через ODBC можно заменить параметр соединения `Servername`. В этом случае, значения `USERNAME` и `PASSWORD` из `odbc.ini` отправляются на удаленный сервер и могут быть скомпроментированы. **Пример небезопасного использования** @@ -150,7 +150,7 @@ PASSWORD = test SELECT * FROM odbc('DSN=gregtest;Servername=some-server.com', 'test_db'); ``` -то ODBC драйвер отправит значения `USERNAME` и `PASSWORD` для `Servername = localhost` на `some-server.com`. +то ODBC драйвер отправит значения `USERNAME` и `PASSWORD` из `odbc.ini` на `some-server.com`. ### Пример подключения PostgreSQL From 79b6b8476a7300edc4ade97fab07700894b74133 Mon Sep 17 00:00:00 2001 From: VadimPE Date: Mon, 8 Oct 2018 18:05:23 +0300 Subject: [PATCH 32/78] CLICKHOUSE-3947 add tests --- dbms/tests/integration/test_storage_mysql/test.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/dbms/tests/integration/test_storage_mysql/test.py b/dbms/tests/integration/test_storage_mysql/test.py index 97aca105c74..dab1110d1eb 100644 --- a/dbms/tests/integration/test_storage_mysql/test.py +++ b/dbms/tests/integration/test_storage_mysql/test.py @@ -76,6 +76,21 @@ CREATE TABLE {}(id UInt32, name String, age UInt32, money UInt32) ENGINE = MySQL assert node1.query("SELECT sum(money) FROM {}".format(table_name)).rstrip() == '60000' conn.close() +def test_where(started_cluster): + table_name = 'test_where' + conn = get_mysql_conn() + create_mysql_table(conn, table_name) + node1.query(''' +CREATE TABLE {}(id UInt32, name String, age UInt32, money UInt32) ENGINE = MySQL('mysql1:3306', 'clickhouse', '{}', 'root', 'clickhouse'); +'''.format(table_name, table_name)) + node1.query("INSERT INTO {}(id, name, money) select number, concat('name_', toString(number)), 3 from numbers(10000) ".format(table_name)) + assert node1.query("SELECT count() FROM {} WHERE name LIKE '%name_%'".format(table_name)).rstrip() == '10000' + assert node1.query("SELECT count() FROM {} WHERE name NOT LIKE '%tmp_%'".format(table_name)).rstrip() == '10000' + assert node1.query("SELECT count() FROM {} WHERE money IN (1, 2, 3)".format(table_name)).rstrip() == '10000' + assert node1.query("SELECT count() FROM {} WHERE money IN (1, 2, 4, 5, 6)".format(table_name)).rstrip() == '0' + assert node1.query("SELECT count() FROM {} WHERE money NOT IN (1, 2, 4, 5, 6)".format(table_name)).rstrip() == '10000' + assert node1.query("SELECT count() FROM {} WHERE name LIKE concat('name_', toString(1))".format(table_name)).rstrip() == '1' + conn.close() def get_mysql_conn(): conn = pymysql.connect(user='root', password='clickhouse', host='127.0.0.1', port=3308) From f21c4c898e13768736c9726196d0d27101605c2b Mon Sep 17 00:00:00 2001 From: chertus Date: Mon, 8 Oct 2018 22:45:17 +0300 Subject: [PATCH 33/78] Decimal dictionaries support [CLICKHOUSE-4045] --- dbms/src/Columns/ColumnDecimal.cpp | 8 +- dbms/src/Columns/ColumnDecimal.h | 1 + dbms/src/Dictionaries/CacheDictionary.cpp | 54 +++++++++-- dbms/src/Dictionaries/CacheDictionary.h | 26 +++-- .../Dictionaries/ComplexKeyCacheDictionary.h | 29 +++++- ...acheDictionary_createAttributeWithType.cpp | 15 +++ .../ComplexKeyCacheDictionary_generate1.cpp | 5 +- .../ComplexKeyCacheDictionary_generate2.cpp | 5 +- .../ComplexKeyCacheDictionary_generate3.cpp | 5 +- ...exKeyCacheDictionary_setAttributeValue.cpp | 5 + ...cheDictionary_setDefaultAttributeValue.cpp | 11 +++ .../ComplexKeyHashedDictionary.cpp | 41 +++++++- .../Dictionaries/ComplexKeyHashedDictionary.h | 21 ++++- .../Dictionaries/DictionaryBlockInputStream.h | 94 ++++++++++++++++--- dbms/src/Dictionaries/DictionaryStructure.cpp | 14 +++ dbms/src/Dictionaries/DictionaryStructure.h | 3 + dbms/src/Dictionaries/FlatDictionary.cpp | 36 ++++++- dbms/src/Dictionaries/FlatDictionary.h | 21 ++++- dbms/src/Dictionaries/HashedDictionary.cpp | 41 +++++++- dbms/src/Dictionaries/HashedDictionary.h | 21 ++++- .../Dictionaries/MongoDBDictionarySource.cpp | 3 + .../RangeDictionaryBlockInputStream.h | 38 ++++++-- .../Dictionaries/RangeHashedDictionary.cpp | 35 ++++++- dbms/src/Dictionaries/RangeHashedDictionary.h | 11 ++- dbms/src/Dictionaries/TrieDictionary.cpp | 37 +++++++- dbms/src/Dictionaries/TrieDictionary.h | 21 ++++- 26 files changed, 533 insertions(+), 68 deletions(-) diff --git a/dbms/src/Columns/ColumnDecimal.cpp b/dbms/src/Columns/ColumnDecimal.cpp index 1bba42a6649..99e1dc253a6 100644 --- a/dbms/src/Columns/ColumnDecimal.cpp +++ b/dbms/src/Columns/ColumnDecimal.cpp @@ -1,5 +1,5 @@ -//#include #include +#include #include #include @@ -47,6 +47,12 @@ const char * ColumnDecimal::deserializeAndInsertFromArena(const char * pos) return pos + sizeof(T); } +template +UInt64 ColumnDecimal::get64(size_t n) const +{ + return ext::bit_cast(data[n]); +} + template void ColumnDecimal::updateHashWithValue(size_t n, SipHash & hash) const { diff --git a/dbms/src/Columns/ColumnDecimal.h b/dbms/src/Columns/ColumnDecimal.h index e580302f591..e2e52bfe8ed 100644 --- a/dbms/src/Columns/ColumnDecimal.h +++ b/dbms/src/Columns/ColumnDecimal.h @@ -111,6 +111,7 @@ public: void get(size_t n, Field & res) const override { res = (*this)[n]; } bool getBool(size_t n) const override { return bool(data[n]); } Int64 getInt(size_t n) const override { return Int64(data[n] * scale); } + UInt64 get64(size_t n) const override; ColumnPtr filter(const IColumn::Filter & filt, ssize_t result_size_hint) const override; ColumnPtr permute(const IColumn::Permutation & perm, size_t limit) const override; diff --git a/dbms/src/Dictionaries/CacheDictionary.cpp b/dbms/src/Dictionaries/CacheDictionary.cpp index 6e5080ec2d5..9063c19be01 100644 --- a/dbms/src/Dictionaries/CacheDictionary.cpp +++ b/dbms/src/Dictionaries/CacheDictionary.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -209,7 +208,7 @@ void CacheDictionary::isInConstantVector( #define DECLARE(TYPE)\ -void CacheDictionary::get##TYPE(const std::string & attribute_name, const PaddedPODArray & ids, PaddedPODArray & out) const\ +void CacheDictionary::get##TYPE(const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out) const\ {\ auto & attribute = getAttribute(attribute_name);\ if (!isAttributeTypeConvertibleTo(attribute.type, AttributeUnderlyingType::TYPE))\ @@ -230,6 +229,9 @@ DECLARE(Int32) DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) +DECLARE(Decimal32) +DECLARE(Decimal64) +DECLARE(Decimal128) #undef DECLARE void CacheDictionary::getString(const std::string & attribute_name, const PaddedPODArray & ids, ColumnString * out) const @@ -246,7 +248,7 @@ void CacheDictionary::getString(const std::string & attribute_name, const Padded #define DECLARE(TYPE)\ void CacheDictionary::get##TYPE(\ const std::string & attribute_name, const PaddedPODArray & ids, const PaddedPODArray & def,\ - PaddedPODArray & out) const\ + ResultArrayType & out) const\ {\ auto & attribute = getAttribute(attribute_name);\ if (!isAttributeTypeConvertibleTo(attribute.type, AttributeUnderlyingType::TYPE))\ @@ -265,6 +267,9 @@ DECLARE(Int32) DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) +DECLARE(Decimal32) +DECLARE(Decimal64) +DECLARE(Decimal128) #undef DECLARE void CacheDictionary::getString( @@ -280,7 +285,7 @@ void CacheDictionary::getString( #define DECLARE(TYPE)\ void CacheDictionary::get##TYPE(\ - const std::string & attribute_name, const PaddedPODArray & ids, const TYPE def, PaddedPODArray & out) const\ + const std::string & attribute_name, const PaddedPODArray & ids, const TYPE def, ResultArrayType & out) const\ {\ auto & attribute = getAttribute(attribute_name);\ if (!isAttributeTypeConvertibleTo(attribute.type, AttributeUnderlyingType::TYPE))\ @@ -299,6 +304,9 @@ DECLARE(Int32) DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) +DECLARE(Decimal32) +DECLARE(Decimal64) +DECLARE(Decimal128) #undef DECLARE void CacheDictionary::getString( @@ -491,6 +499,21 @@ CacheDictionary::Attribute CacheDictionary::createAttributeWithType(const Attrib std::get>(attr.arrays) = std::make_unique>(size); bytes_allocated += size * sizeof(Int64); break; + case AttributeUnderlyingType::Decimal32: + std::get(attr.null_values) = null_value.get(); + std::get>(attr.arrays) = std::make_unique>(size); + bytes_allocated += size * sizeof(Decimal32); + break; + case AttributeUnderlyingType::Decimal64: + std::get(attr.null_values) = null_value.get(); + std::get>(attr.arrays) = std::make_unique>(size); + bytes_allocated += size * sizeof(Decimal64); + break; + case AttributeUnderlyingType::Decimal128: + std::get(attr.null_values) = null_value.get(); + std::get>(attr.arrays) = std::make_unique>(size); + bytes_allocated += size * sizeof(Decimal128); + break; case AttributeUnderlyingType::Float32: std::get(attr.null_values) = null_value.get(); std::get>(attr.arrays) = std::make_unique>(size); @@ -518,7 +541,7 @@ template void CacheDictionary::getItemsNumber( Attribute & attribute, const PaddedPODArray & ids, - PaddedPODArray & out, + ResultArrayType & out, DefaultGetter && get_default) const { if (false) {} @@ -536,6 +559,9 @@ void CacheDictionary::getItemsNumber( DISPATCH(Int64) DISPATCH(Float32) DISPATCH(Float64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) #undef DISPATCH else throw Exception("Unexpected type of attribute: " + toString(attribute.type), ErrorCodes::LOGICAL_ERROR); @@ -545,7 +571,7 @@ template void CacheDictionary::getItemsNumberImpl( Attribute & attribute, const PaddedPODArray & ids, - PaddedPODArray & out, + ResultArrayType & out, DefaultGetter && get_default) const { /// Mapping: -> { all indices `i` of `ids` such that `ids[i]` = } @@ -893,6 +919,17 @@ void CacheDictionary::setDefaultAttributeValue(Attribute & attribute, const Key case AttributeUnderlyingType::Int64: std::get>(attribute.arrays)[idx] = std::get(attribute.null_values); break; case AttributeUnderlyingType::Float32: std::get>(attribute.arrays)[idx] = std::get(attribute.null_values); break; case AttributeUnderlyingType::Float64: std::get>(attribute.arrays)[idx] = std::get(attribute.null_values); break; + + case AttributeUnderlyingType::Decimal32: + std::get>(attribute.arrays)[idx] = std::get(attribute.null_values); + break; + case AttributeUnderlyingType::Decimal64: + std::get>(attribute.arrays)[idx] = std::get(attribute.null_values); + break; + case AttributeUnderlyingType::Decimal128: + std::get>(attribute.arrays)[idx] = std::get(attribute.null_values); + break; + case AttributeUnderlyingType::String: { const auto & null_value_ref = std::get(attribute.null_values); @@ -926,6 +963,11 @@ void CacheDictionary::setAttributeValue(Attribute & attribute, const Key idx, co case AttributeUnderlyingType::Int64: std::get>(attribute.arrays)[idx] = value.get(); break; case AttributeUnderlyingType::Float32: std::get>(attribute.arrays)[idx] = value.get(); break; case AttributeUnderlyingType::Float64: std::get>(attribute.arrays)[idx] = value.get(); break; + + case AttributeUnderlyingType::Decimal32: std::get>(attribute.arrays)[idx] = value.get(); break; + case AttributeUnderlyingType::Decimal64: std::get>(attribute.arrays)[idx] = value.get(); break; + case AttributeUnderlyingType::Decimal128: std::get>(attribute.arrays)[idx] = value.get(); break; + case AttributeUnderlyingType::String: { const auto & string = value.get(); diff --git a/dbms/src/Dictionaries/CacheDictionary.h b/dbms/src/Dictionaries/CacheDictionary.h index 15568e164f7..373e3e70643 100644 --- a/dbms/src/Dictionaries/CacheDictionary.h +++ b/dbms/src/Dictionaries/CacheDictionary.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -80,8 +81,11 @@ public: void isInVectorConstant(const PaddedPODArray & child_ids, const Key ancestor_id, PaddedPODArray & out) const override; void isInConstantVector(const Key child_id, const PaddedPODArray & ancestor_ids, PaddedPODArray & out) const override; + template + using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; + #define DECLARE(TYPE)\ - void get##TYPE(const std::string & attribute_name, const PaddedPODArray & ids, PaddedPODArray & out) const; + void get##TYPE(const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out) const; DECLARE(UInt8) DECLARE(UInt16) DECLARE(UInt32) @@ -93,6 +97,9 @@ public: DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) #undef DECLARE void getString(const std::string & attribute_name, const PaddedPODArray & ids, ColumnString * out) const; @@ -100,7 +107,7 @@ public: #define DECLARE(TYPE)\ void get##TYPE(\ const std::string & attribute_name, const PaddedPODArray & ids, const PaddedPODArray & def,\ - PaddedPODArray & out) const; + ResultArrayType & out) const; DECLARE(UInt8) DECLARE(UInt16) DECLARE(UInt32) @@ -112,6 +119,9 @@ public: DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) #undef DECLARE void getString( @@ -119,8 +129,7 @@ public: ColumnString * const out) const; #define DECLARE(TYPE)\ - void get##TYPE(\ - const std::string & attribute_name, const PaddedPODArray & ids, const TYPE def, PaddedPODArray & out) const; + void get##TYPE(const std::string & attribute_name, const PaddedPODArray & ids, const TYPE def, ResultArrayType & out) const; DECLARE(UInt8) DECLARE(UInt16) DECLARE(UInt32) @@ -132,6 +141,9 @@ public: DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) #undef DECLARE void getString( @@ -174,12 +186,14 @@ private: UInt8, UInt16, UInt32, UInt64, UInt128, Int8, Int16, Int32, Int64, + Decimal32, Decimal64, Decimal128, Float32, Float64, String> null_values; std::tuple< ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, + ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType> arrays; }; @@ -193,14 +207,14 @@ private: void getItemsNumber( Attribute & attribute, const PaddedPODArray & ids, - PaddedPODArray & out, + ResultArrayType & out, DefaultGetter && get_default) const; template void getItemsNumberImpl( Attribute & attribute, const PaddedPODArray & ids, - PaddedPODArray & out, + ResultArrayType & out, DefaultGetter && get_default) const; template diff --git a/dbms/src/Dictionaries/ComplexKeyCacheDictionary.h b/dbms/src/Dictionaries/ComplexKeyCacheDictionary.h index 0c71de5f79c..bcfbb65411e 100644 --- a/dbms/src/Dictionaries/ComplexKeyCacheDictionary.h +++ b/dbms/src/Dictionaries/ComplexKeyCacheDictionary.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -128,11 +129,14 @@ public: return dict_struct.attributes[&getAttribute(attribute_name) - attributes.data()].injective; } + template + using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; + /// In all functions below, key_columns must be full (non-constant) columns. /// See the requirement in IDataType.h for text-serialization functions. #define DECLARE(TYPE) \ void get##TYPE( \ - const std::string & attribute_name, const Columns & key_columns, const DataTypes & key_types, PaddedPODArray & out) const; + const std::string & attribute_name, const Columns & key_columns, const DataTypes & key_types, ResultArrayType & out) const; DECLARE(UInt8) DECLARE(UInt16) DECLARE(UInt32) @@ -144,6 +148,9 @@ public: DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) #undef DECLARE void getString(const std::string & attribute_name, const Columns & key_columns, const DataTypes & key_types, ColumnString * out) const; @@ -153,7 +160,7 @@ public: const Columns & key_columns, \ const DataTypes & key_types, \ const PaddedPODArray & def, \ - PaddedPODArray & out) const; + ResultArrayType & out) const; DECLARE(UInt8) DECLARE(UInt16) DECLARE(UInt32) @@ -165,6 +172,9 @@ public: DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) #undef DECLARE void getString(const std::string & attribute_name, @@ -178,7 +188,7 @@ public: const Columns & key_columns, \ const DataTypes & key_types, \ const TYPE def, \ - PaddedPODArray & out) const; + ResultArrayType & out) const; DECLARE(UInt8) DECLARE(UInt16) DECLARE(UInt32) @@ -190,6 +200,9 @@ public: DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) #undef DECLARE void getString(const std::string & attribute_name, @@ -247,7 +260,9 @@ private: struct Attribute final { AttributeUnderlyingType type; - std::tuple null_values; + std::tuple null_values; std::tuple, ContainerPtrType, ContainerPtrType, @@ -257,6 +272,9 @@ private: ContainerPtrType, ContainerPtrType, ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType> @@ -288,6 +306,9 @@ private: DISPATCH(Int64) DISPATCH(Float32) DISPATCH(Float64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) #undef DISPATCH else throw Exception("Unexpected type of attribute: " + toString(attribute.type), ErrorCodes::LOGICAL_ERROR); } diff --git a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_createAttributeWithType.cpp b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_createAttributeWithType.cpp index 809753f6cb6..52770d37c6a 100644 --- a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_createAttributeWithType.cpp +++ b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_createAttributeWithType.cpp @@ -64,6 +64,21 @@ ComplexKeyCacheDictionary::Attribute ComplexKeyCacheDictionary::createAttributeW std::get>(attr.arrays) = std::make_unique>(size); bytes_allocated += size * sizeof(Float64); break; + case AttributeUnderlyingType::Decimal32: + std::get(attr.null_values) = null_value.get(); + std::get>(attr.arrays) = std::make_unique>(size); + bytes_allocated += size * sizeof(Decimal32); + break; + case AttributeUnderlyingType::Decimal64: + std::get(attr.null_values) = null_value.get(); + std::get>(attr.arrays) = std::make_unique>(size); + bytes_allocated += size * sizeof(Decimal64); + break; + case AttributeUnderlyingType::Decimal128: + std::get(attr.null_values) = null_value.get(); + std::get>(attr.arrays) = std::make_unique>(size); + bytes_allocated += size * sizeof(Decimal128); + break; case AttributeUnderlyingType::String: std::get(attr.null_values) = null_value.get(); std::get>(attr.arrays) = std::make_unique>(size); diff --git a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_generate1.cpp b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_generate1.cpp index 89373470c27..c22c14bc267 100644 --- a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_generate1.cpp +++ b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_generate1.cpp @@ -9,7 +9,7 @@ namespace ErrorCodes #define DECLARE(TYPE) \ void ComplexKeyCacheDictionary::get##TYPE( \ - const std::string & attribute_name, const Columns & key_columns, const DataTypes & key_types, PaddedPODArray & out) const \ + const std::string & attribute_name, const Columns & key_columns, const DataTypes & key_types, ResultArrayType & out) const \ { \ dict_struct.validateKeyTypes(key_types); \ \ @@ -33,5 +33,8 @@ DECLARE(Int32) DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) +DECLARE(Decimal32) +DECLARE(Decimal64) +DECLARE(Decimal128) #undef DECLARE } diff --git a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_generate2.cpp b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_generate2.cpp index 589f9a6f222..8b7df84288d 100644 --- a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_generate2.cpp +++ b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_generate2.cpp @@ -12,7 +12,7 @@ namespace ErrorCodes const Columns & key_columns, \ const DataTypes & key_types, \ const PaddedPODArray & def, \ - PaddedPODArray & out) const \ + ResultArrayType & out) const \ { \ dict_struct.validateKeyTypes(key_types); \ \ @@ -34,5 +34,8 @@ DECLARE(Int32) DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) +DECLARE(Decimal32) +DECLARE(Decimal64) +DECLARE(Decimal128) #undef DECLARE } diff --git a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_generate3.cpp b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_generate3.cpp index 5bf6ba5f581..ecc8794554b 100644 --- a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_generate3.cpp +++ b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_generate3.cpp @@ -12,7 +12,7 @@ namespace ErrorCodes const Columns & key_columns, \ const DataTypes & key_types, \ const TYPE def, \ - PaddedPODArray & out) const \ + ResultArrayType & out) const \ { \ dict_struct.validateKeyTypes(key_types); \ \ @@ -34,5 +34,8 @@ DECLARE(Int32) DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) +DECLARE(Decimal32) +DECLARE(Decimal64) +DECLARE(Decimal128) #undef DECLARE } diff --git a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_setAttributeValue.cpp b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_setAttributeValue.cpp index 22cf1ae33c2..e85f96de420 100644 --- a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_setAttributeValue.cpp +++ b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_setAttributeValue.cpp @@ -18,6 +18,11 @@ void ComplexKeyCacheDictionary::setAttributeValue(Attribute & attribute, const s case AttributeUnderlyingType::Int64: std::get>(attribute.arrays)[idx] = value.get(); break; case AttributeUnderlyingType::Float32: std::get>(attribute.arrays)[idx] = value.get(); break; case AttributeUnderlyingType::Float64: std::get>(attribute.arrays)[idx] = value.get(); break; + + case AttributeUnderlyingType::Decimal32: std::get>(attribute.arrays)[idx] = value.get(); break; + case AttributeUnderlyingType::Decimal64: std::get>(attribute.arrays)[idx] = value.get(); break; + case AttributeUnderlyingType::Decimal128: std::get>(attribute.arrays)[idx] = value.get(); break; + case AttributeUnderlyingType::String: { const auto & string = value.get(); diff --git a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_setDefaultAttributeValue.cpp b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_setDefaultAttributeValue.cpp index d8e12ba56ea..e3af300767d 100644 --- a/dbms/src/Dictionaries/ComplexKeyCacheDictionary_setDefaultAttributeValue.cpp +++ b/dbms/src/Dictionaries/ComplexKeyCacheDictionary_setDefaultAttributeValue.cpp @@ -18,6 +18,17 @@ void ComplexKeyCacheDictionary::setDefaultAttributeValue(Attribute & attribute, case AttributeUnderlyingType::Int64: std::get>(attribute.arrays)[idx] = std::get(attribute.null_values); break; case AttributeUnderlyingType::Float32: std::get>(attribute.arrays)[idx] = std::get(attribute.null_values); break; case AttributeUnderlyingType::Float64: std::get>(attribute.arrays)[idx] = std::get(attribute.null_values); break; + + case AttributeUnderlyingType::Decimal32: + std::get>(attribute.arrays)[idx] = std::get(attribute.null_values); + break; + case AttributeUnderlyingType::Decimal64: + std::get>(attribute.arrays)[idx] = std::get(attribute.null_values); + break; + case AttributeUnderlyingType::Decimal128: + std::get>(attribute.arrays)[idx] = std::get(attribute.null_values); + break; + case AttributeUnderlyingType::String: { const auto & null_value_ref = std::get(attribute.null_values); diff --git a/dbms/src/Dictionaries/ComplexKeyHashedDictionary.cpp b/dbms/src/Dictionaries/ComplexKeyHashedDictionary.cpp index 8ca3f10ef2a..96d50d2b904 100644 --- a/dbms/src/Dictionaries/ComplexKeyHashedDictionary.cpp +++ b/dbms/src/Dictionaries/ComplexKeyHashedDictionary.cpp @@ -45,7 +45,7 @@ ComplexKeyHashedDictionary::ComplexKeyHashedDictionary(const ComplexKeyHashedDic #define DECLARE(TYPE)\ void ComplexKeyHashedDictionary::get##TYPE(\ const std::string & attribute_name, const Columns & key_columns, const DataTypes & key_types,\ - PaddedPODArray & out) const\ + ResultArrayType & out) const\ {\ dict_struct.validateKeyTypes(key_types);\ \ @@ -70,6 +70,9 @@ DECLARE(Int32) DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) +DECLARE(Decimal32) +DECLARE(Decimal64) +DECLARE(Decimal128) #undef DECLARE void ComplexKeyHashedDictionary::getString( @@ -92,7 +95,7 @@ void ComplexKeyHashedDictionary::getString( #define DECLARE(TYPE)\ void ComplexKeyHashedDictionary::get##TYPE(\ const std::string & attribute_name, const Columns & key_columns, const DataTypes & key_types,\ - const PaddedPODArray & def, PaddedPODArray & out) const\ + const PaddedPODArray & def, ResultArrayType & out) const\ {\ dict_struct.validateKeyTypes(key_types);\ \ @@ -115,6 +118,9 @@ DECLARE(Int32) DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) +DECLARE(Decimal32) +DECLARE(Decimal64) +DECLARE(Decimal128) #undef DECLARE void ComplexKeyHashedDictionary::getString( @@ -135,7 +141,7 @@ void ComplexKeyHashedDictionary::getString( #define DECLARE(TYPE)\ void ComplexKeyHashedDictionary::get##TYPE(\ const std::string & attribute_name, const Columns & key_columns, const DataTypes & key_types,\ - const TYPE def, PaddedPODArray & out) const\ + const TYPE def, ResultArrayType & out) const\ {\ dict_struct.validateKeyTypes(key_types);\ \ @@ -158,6 +164,9 @@ DECLARE(Int32) DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) +DECLARE(Decimal32) +DECLARE(Decimal64) +DECLARE(Decimal128) #undef DECLARE void ComplexKeyHashedDictionary::getString( @@ -195,6 +204,10 @@ void ComplexKeyHashedDictionary::has(const Columns & key_columns, const DataType case AttributeUnderlyingType::Float32: has(attribute, key_columns, out); break; case AttributeUnderlyingType::Float64: has(attribute, key_columns, out); break; case AttributeUnderlyingType::String: has(attribute, key_columns, out); break; + + case AttributeUnderlyingType::Decimal32: has(attribute, key_columns, out); break; + case AttributeUnderlyingType::Decimal64: has(attribute, key_columns, out); break; + case AttributeUnderlyingType::Decimal128: has(attribute, key_columns, out); break; } } @@ -387,6 +400,11 @@ void ComplexKeyHashedDictionary::calculateBytesAllocated() case AttributeUnderlyingType::Int64: addAttributeSize(attribute); break; case AttributeUnderlyingType::Float32: addAttributeSize(attribute); break; case AttributeUnderlyingType::Float64: addAttributeSize(attribute); break; + + case AttributeUnderlyingType::Decimal32: addAttributeSize(attribute); break; + case AttributeUnderlyingType::Decimal64: addAttributeSize(attribute); break; + case AttributeUnderlyingType::Decimal128: addAttributeSize(attribute); break; + case AttributeUnderlyingType::String: { addAttributeSize(attribute); @@ -424,6 +442,11 @@ ComplexKeyHashedDictionary::Attribute ComplexKeyHashedDictionary::createAttribut case AttributeUnderlyingType::Int64: createAttributeImpl(attr, null_value); break; case AttributeUnderlyingType::Float32: createAttributeImpl(attr, null_value); break; case AttributeUnderlyingType::Float64: createAttributeImpl(attr, null_value); break; + + case AttributeUnderlyingType::Decimal32: createAttributeImpl(attr, null_value); break; + case AttributeUnderlyingType::Decimal64: createAttributeImpl(attr, null_value); break; + case AttributeUnderlyingType::Decimal128: createAttributeImpl(attr, null_value); break; + case AttributeUnderlyingType::String: { std::get(attr.null_values) = null_value.get(); @@ -459,6 +482,9 @@ void ComplexKeyHashedDictionary::getItemsNumber( DISPATCH(Int64) DISPATCH(Float32) DISPATCH(Float64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) #undef DISPATCH else throw Exception("Unexpected type of attribute: " + toString(attribute.type), ErrorCodes::LOGICAL_ERROR); @@ -517,6 +543,11 @@ bool ComplexKeyHashedDictionary::setAttributeValue(Attribute & attribute, const case AttributeUnderlyingType::Int64: return setAttributeValueImpl(attribute, key, value.get()); case AttributeUnderlyingType::Float32: return setAttributeValueImpl(attribute, key, value.get()); case AttributeUnderlyingType::Float64: return setAttributeValueImpl(attribute, key, value.get()); + + case AttributeUnderlyingType::Decimal32: return setAttributeValueImpl(attribute, key, value.get()); + case AttributeUnderlyingType::Decimal64: return setAttributeValueImpl(attribute, key, value.get()); + case AttributeUnderlyingType::Decimal128: return setAttributeValueImpl(attribute, key, value.get()); + case AttributeUnderlyingType::String: { auto & map = *std::get>(attribute.maps); @@ -604,6 +635,10 @@ std::vector ComplexKeyHashedDictionary::getKeys() const case AttributeUnderlyingType::Float32: return getKeys(attribute); case AttributeUnderlyingType::Float64: return getKeys(attribute); case AttributeUnderlyingType::String: return getKeys(attribute); + + case AttributeUnderlyingType::Decimal32: return getKeys(attribute); + case AttributeUnderlyingType::Decimal64: return getKeys(attribute); + case AttributeUnderlyingType::Decimal128: return getKeys(attribute); } return {}; } diff --git a/dbms/src/Dictionaries/ComplexKeyHashedDictionary.h b/dbms/src/Dictionaries/ComplexKeyHashedDictionary.h index 181f6e90f4f..40a2c7174c8 100644 --- a/dbms/src/Dictionaries/ComplexKeyHashedDictionary.h +++ b/dbms/src/Dictionaries/ComplexKeyHashedDictionary.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -65,10 +66,13 @@ public: return dict_struct.attributes[&getAttribute(attribute_name) - attributes.data()].injective; } + template + using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; + #define DECLARE(TYPE)\ void get##TYPE(\ const std::string & attribute_name, const Columns & key_columns, const DataTypes & key_types,\ - PaddedPODArray & out) const; + ResultArrayType & out) const; DECLARE(UInt8) DECLARE(UInt16) DECLARE(UInt32) @@ -80,6 +84,9 @@ public: DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) #undef DECLARE void getString( @@ -89,7 +96,7 @@ public: #define DECLARE(TYPE)\ void get##TYPE(\ const std::string & attribute_name, const Columns & key_columns, const DataTypes & key_types,\ - const PaddedPODArray & def, PaddedPODArray & out) const; + const PaddedPODArray & def, ResultArrayType & out) const; DECLARE(UInt8) DECLARE(UInt16) DECLARE(UInt32) @@ -101,6 +108,9 @@ public: DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) #undef DECLARE void getString( @@ -110,7 +120,7 @@ public: #define DECLARE(TYPE)\ void get##TYPE(\ const std::string & attribute_name, const Columns & key_columns, const DataTypes & key_types,\ - const TYPE def, PaddedPODArray & out) const; + const TYPE def, ResultArrayType & out) const; DECLARE(UInt8) DECLARE(UInt16) DECLARE(UInt32) @@ -122,6 +132,9 @@ public: DECLARE(Int64) DECLARE(Float32) DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) #undef DECLARE void getString( @@ -143,12 +156,14 @@ private: UInt8, UInt16, UInt32, UInt64, UInt128, Int8, Int16, Int32, Int64, + Decimal32, Decimal64, Decimal128, Float32, Float64, String> null_values; std::tuple< ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, + ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType, ContainerPtrType> maps; std::unique_ptr string_arena; diff --git a/dbms/src/Dictionaries/DictionaryBlockInputStream.h b/dbms/src/Dictionaries/DictionaryBlockInputStream.h index 158806dc686..83491e5a699 100644 --- a/dbms/src/Dictionaries/DictionaryBlockInputStream.h +++ b/dbms/src/Dictionaries/DictionaryBlockInputStream.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -63,12 +64,20 @@ private: template using DictionaryGetter = void (DictionaryType::*)(const std::string &, const PaddedPODArray &, PaddedPODArray &) const; + template + using DictionaryDecimalGetter = + void (DictionaryType::*)(const std::string &, const PaddedPODArray &, DecimalPaddedPODArray &) const; + using DictionaryStringGetter = void (DictionaryType::*)(const std::string &, const PaddedPODArray &, ColumnString *) const; // for complex complex key dictionaries template using GetterByKey = void (DictionaryType::*)(const std::string &, const Columns &, const DataTypes &, PaddedPODArray & out) const; + template + using DecimalGetterByKey = + void (DictionaryType::*)(const std::string &, const Columns &, const DataTypes &, DecimalPaddedPODArray & out) const; + using StringGetterByKey = void (DictionaryType::*)(const std::string &, const Columns &, const DataTypes &, ColumnString * out) const; // call getXXX @@ -78,6 +87,11 @@ private: const Columns & keys, const DataTypes & data_types, Container & container, const DictionaryAttribute & attribute, const DictionaryType & dictionary) const; + template + void callGetter(DictionaryDecimalGetter getter, const PaddedPODArray & ids_to_fill, + const Columns & keys, const DataTypes & data_types, + Container & container, const DictionaryAttribute & attribute, const DictionaryType & dictionary) const; + template void callGetter(DictionaryStringGetter getter, const PaddedPODArray & ids_to_fill, const Columns & keys, const DataTypes & data_types, @@ -89,12 +103,17 @@ private: const Columns & keys, const DataTypes & data_types, Container & container, const DictionaryAttribute & attribute, const DictionaryType & dictionary) const; + template + void callGetter(DecimalGetterByKey getter, const PaddedPODArray & ids_to_fill, + const Columns & keys, const DataTypes & data_types, + Container & container, const DictionaryAttribute & attribute, const DictionaryType & dictionary) const; + template void callGetter(StringGetterByKey getter, const PaddedPODArray & ids_to_fill, const Columns & keys, const DataTypes & data_types, Container & container, const DictionaryAttribute & attribute, const DictionaryType & dictionary) const; - template