summaryrefslogtreecommitdiffstats
path: root/yql/essentials/docs/en
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2025-01-16 16:50:58 +0300
committerrobot-piglet <[email protected]>2025-01-16 17:03:02 +0300
commitb97da5f54c0edb261f74824aa9a8a643a4ae75f6 (patch)
tree2ea3dc77615160742cba9eb802c81191e62ab650 /yql/essentials/docs/en
parent7e86bcaf10a418760708b0be68e02abd715b745b (diff)
Intermediate changes
commit_hash:aad4c6091d19bafc760b4b238153622f73166199
Diffstat (limited to 'yql/essentials/docs/en')
-rw-r--r--yql/essentials/docs/en/_includes/cast_examples.md16
-rw-r--r--yql/essentials/docs/en/_includes/decimal_args.md5
-rw-r--r--yql/essentials/docs/en/builtins/aggregation.md615
-rw-r--r--yql/essentials/docs/en/builtins/basic.md1213
-rw-r--r--yql/essentials/docs/en/builtins/codegen.md181
-rw-r--r--yql/essentials/docs/en/builtins/dict.md282
-rw-r--r--yql/essentials/docs/en/builtins/index.md12
-rw-r--r--yql/essentials/docs/en/builtins/json.md1170
-rw-r--r--yql/essentials/docs/en/builtins/list.md602
-rw-r--r--yql/essentials/docs/en/builtins/struct.md359
-rw-r--r--yql/essentials/docs/en/builtins/toc_i.yaml13
-rw-r--r--yql/essentials/docs/en/builtins/types.md637
-rw-r--r--yql/essentials/docs/en/builtins/window.md247
-rw-r--r--yql/essentials/docs/en/index.md10
-rw-r--r--yql/essentials/docs/en/recipes/accessing-json.md103
-rw-r--r--yql/essentials/docs/en/recipes/index.md9
-rw-r--r--yql/essentials/docs/en/recipes/modifying-json.md23
-rw-r--r--yql/essentials/docs/en/recipes/toc_i.yaml7
-rw-r--r--yql/essentials/docs/en/syntax/_assets/join-YQL-06.pngbin0 -> 24792 bytes
-rw-r--r--yql/essentials/docs/en/syntax/action.md145
-rw-r--r--yql/essentials/docs/en/syntax/commit.md17
-rw-r--r--yql/essentials/docs/en/syntax/declare.md36
-rw-r--r--yql/essentials/docs/en/syntax/discard.md29
-rw-r--r--yql/essentials/docs/en/syntax/export_import.md63
-rw-r--r--yql/essentials/docs/en/syntax/expressions.md550
-rw-r--r--yql/essentials/docs/en/syntax/flatten.md145
-rw-r--r--yql/essentials/docs/en/syntax/group_by.md267
-rw-r--r--yql/essentials/docs/en/syntax/index.md22
-rw-r--r--yql/essentials/docs/en/syntax/insert_into.md33
-rw-r--r--yql/essentials/docs/en/syntax/into_result.md16
-rw-r--r--yql/essentials/docs/en/syntax/join.md143
-rw-r--r--yql/essentials/docs/en/syntax/lexer.md235
-rw-r--r--yql/essentials/docs/en/syntax/not_yet_supported.md14
-rw-r--r--yql/essentials/docs/en/syntax/pragma.md421
-rw-r--r--yql/essentials/docs/en/syntax/process.md78
-rw-r--r--yql/essentials/docs/en/syntax/reduce.md108
-rw-r--r--yql/essentials/docs/en/syntax/select/assume_order_by.md14
-rw-r--r--yql/essentials/docs/en/syntax/select/concat.md44
-rw-r--r--yql/essentials/docs/en/syntax/select/distinct.md19
-rw-r--r--yql/essentials/docs/en/syntax/select/from.md22
-rw-r--r--yql/essentials/docs/en/syntax/select/from_as_table.md17
-rw-r--r--yql/essentials/docs/en/syntax/select/from_select.md17
-rw-r--r--yql/essentials/docs/en/syntax/select/index.md102
-rw-r--r--yql/essentials/docs/en/syntax/select/limit_offset.md22
-rw-r--r--yql/essentials/docs/en/syntax/select/order_by.md20
-rw-r--r--yql/essentials/docs/en/syntax/select/sample.md38
-rw-r--r--yql/essentials/docs/en/syntax/select/toc_i.yaml17
-rw-r--r--yql/essentials/docs/en/syntax/select/toc_p.yaml2
-rw-r--r--yql/essentials/docs/en/syntax/select/union.md63
-rw-r--r--yql/essentials/docs/en/syntax/select/unique_distinct_hints.md10
-rw-r--r--yql/essentials/docs/en/syntax/select/where.md11
-rw-r--r--yql/essentials/docs/en/syntax/select/with.md32
-rw-r--r--yql/essentials/docs/en/syntax/select/without.md16
-rw-r--r--yql/essentials/docs/en/syntax/subquery.md190
-rw-r--r--yql/essentials/docs/en/syntax/toc_i.yaml23
-rw-r--r--yql/essentials/docs/en/syntax/use.md24
-rw-r--r--yql/essentials/docs/en/syntax/values.md45
-rw-r--r--yql/essentials/docs/en/syntax/window.md153
-rw-r--r--yql/essentials/docs/en/toc.yaml2
-rw-r--r--yql/essentials/docs/en/toc_i.yaml11
-rw-r--r--yql/essentials/docs/en/types/cast.md74
-rw-r--r--yql/essentials/docs/en/types/containers.md26
-rw-r--r--yql/essentials/docs/en/types/index.md12
-rw-r--r--yql/essentials/docs/en/types/json.md187
-rw-r--r--yql/essentials/docs/en/types/optional.md50
-rw-r--r--yql/essentials/docs/en/types/primitive.md188
-rw-r--r--yql/essentials/docs/en/types/special.md14
-rw-r--r--yql/essentials/docs/en/types/toc_i.yaml17
-rw-r--r--yql/essentials/docs/en/types/type_string.md70
-rw-r--r--yql/essentials/docs/en/udf/list/datetime.md501
-rw-r--r--yql/essentials/docs/en/udf/list/digest.md43
-rw-r--r--yql/essentials/docs/en/udf/list/histogram.md19
-rw-r--r--yql/essentials/docs/en/udf/list/hyperscan.md108
-rw-r--r--yql/essentials/docs/en/udf/list/index.md18
-rw-r--r--yql/essentials/docs/en/udf/list/ip.md47
-rw-r--r--yql/essentials/docs/en/udf/list/math.md140
-rw-r--r--yql/essentials/docs/en/udf/list/pcre.md22
-rw-r--r--yql/essentials/docs/en/udf/list/pire.md115
-rw-r--r--yql/essentials/docs/en/udf/list/re2.md122
-rw-r--r--yql/essentials/docs/en/udf/list/string.md148
-rw-r--r--yql/essentials/docs/en/udf/list/toc_base.yaml16
-rw-r--r--yql/essentials/docs/en/udf/list/toc_i.yaml2
-rw-r--r--yql/essentials/docs/en/udf/list/unicode.md159
-rw-r--r--yql/essentials/docs/en/udf/list/url.md203
-rw-r--r--yql/essentials/docs/en/udf/list/yson.md308
85 files changed, 11349 insertions, 0 deletions
diff --git a/yql/essentials/docs/en/_includes/cast_examples.md b/yql/essentials/docs/en/_includes/cast_examples.md
new file mode 100644
index 00000000000..a7735c3a15b
--- /dev/null
+++ b/yql/essentials/docs/en/_includes/cast_examples.md
@@ -0,0 +1,16 @@
+
+```yql
+SELECT
+ CAST("12345" AS Double), -- 12345.0
+ CAST(1.2345 AS Uint8), -- 1
+ CAST(12345 AS String), -- "12345"
+ CAST("1.2345" AS Decimal(5, 2)), -- 1.23
+ CAST("xyz" AS Uint64) IS NULL, -- true, because it failed
+ CAST(-1 AS Uint16) IS NULL, -- true, a negative integer cast to an unsigned integer
+ CAST([-1, 0, 1] AS List<Uint8?>), -- [null, 0, 1]
+ --The item type is optional: the failed item is cast to null.
+ CAST(["3.14", "bad", "42"] AS List<Float>), -- [3.14, 42]
+ --The item type is not optional: the failed item has been deleted.
+ CAST(255 AS Uint8), -- 255
+ CAST(256 AS Uint8) IS NULL -- true, out of range
+```
diff --git a/yql/essentials/docs/en/_includes/decimal_args.md b/yql/essentials/docs/en/_includes/decimal_args.md
new file mode 100644
index 00000000000..60b818ec591
--- /dev/null
+++ b/yql/essentials/docs/en/_includes/decimal_args.md
@@ -0,0 +1,5 @@
+For the Decimal parametric data type, two additional arguments are specified:
+
+* Total number of decimal places (up to 35, inclusive).
+* Number of places after the decimal point (out of the total number, meaning it can't be larger than the previous argument).
+
diff --git a/yql/essentials/docs/en/builtins/aggregation.md b/yql/essentials/docs/en/builtins/aggregation.md
new file mode 100644
index 00000000000..96a395d61da
--- /dev/null
+++ b/yql/essentials/docs/en/builtins/aggregation.md
@@ -0,0 +1,615 @@
+# Aggregate functions
+
+## COUNT {#count}
+
+Counting the number of rows in the table (if `*` or constant is specified as the argument) or non-empty values in a table column (if the column name is specified as an argument).
+
+Like other aggregate functions, it can be combined with [GROUP BY](../syntax/group_by.md) to get statistics on the parts of the table that correspond to the values in the columns being grouped. Use the modifier [DISTINCT](../syntax/group_by.md#distinct) to count distinct values.
+
+### Examples
+
+```yql
+SELECT COUNT(*) FROM my_table;
+```
+
+```yql
+SELECT key, COUNT(value) FROM my_table GROUP BY key;
+```
+
+```yql
+SELECT COUNT(DISTINCT value) FROM my_table;
+```
+
+## MIN and MAX {#min-max}
+
+Minimum or maximum value.
+
+As an argument, you may use an arbitrary computable expression with a numeric result.
+
+### Examples
+
+```yql
+SELECT MIN(value), MAX(value) FROM my_table;
+```
+
+## SUM {#sum}
+
+Sum of the numbers.
+
+As an argument, you may use an arbitrary computable expression with a numeric result.
+
+Integers are automatically expanded to 64 bits to reduce the risk of overflow.
+
+```yql
+SELECT SUM(value) FROM my_table;
+```
+
+## AVG {#avg}
+
+Arithmetic average.
+
+As an argument, you may use an arbitrary computable expression with a numeric result.
+
+Integer values and time intervals are automatically converted to Double.
+
+### Examples
+
+```yql
+SELECT AVG(value) FROM my_table;
+```
+
+## COUNT_IF {#count-if}
+
+Number of rows for which the expression specified as the argument is true (the expression's calculation result is true).
+
+The value `NULL` is equated to `false` (if the argument type is `Bool?`).
+
+The function *does not* do the implicit type casting to Boolean for strings and numbers.
+
+### Examples
+
+```yql
+SELECT
+ COUNT_IF(value % 2 == 1) AS odd_count
+```
+
+{% note info %}
+
+To count distinct values in rows meeting the condition, unlike other aggregate functions, you can't use the modifier [DISTINCT](../syntax/group_by.md#distinct) because arguments contain no values. To get this result, use in the subquery the built-in function [IF](../builtins/basic.md#if) with two arguments (to get `NULL` in else), and apply an outer [COUNT(DISTINCT ...)](#count) to its result.
+
+{% endnote %}
+
+## SUM_IF and AVG_IF {#sum-if}
+
+Sum or arithmetic average, but only for the rows that satisfy the condition passed by the second argument.
+
+Therefore, `SUM_IF(value, condition)` is a slightly shorter notation for `SUM(IF(condition, value))`, same for `AVG`. The argument's data type expansion is similar to the same-name functions without a suffix.
+
+### Examples
+
+```yql
+SELECT
+ SUM_IF(value, value % 2 == 1) AS odd_sum,
+ AVG_IF(value, value % 2 == 1) AS odd_avg,
+FROM my_table;
+```
+
+When you use [aggregation factories](basic.md#aggregationfactory), a `Tuple` containing a value and a predicate is passed as the first [AGGREGATE_BY](#aggregate-by) argument.
+
+```yql
+$sum_if_factory = AggregationFactory("SUM_IF");
+$avg_if_factory = AggregationFactory("AVG_IF");
+
+SELECT
+ AGGREGATE_BY(AsTuple(value, value % 2 == 1), $sum_if_factory) AS odd_sum,
+ AGGREGATE_BY(AsTuple(value, value % 2 == 1), $avg_if_factory) AS odd_avg
+FROM my_table;
+```
+
+## SOME {#some}
+
+Get the value for an expression specified as an argument, for one of the table rows. Gives no guarantee of which row is used. It's similar to the [any()](https://clickhouse.tech/docs/en/sql-reference/aggregate-functions/reference/any/) function in ClickHouse.
+
+Because of no guarantee, `SOME` is computationally cheaper than [MIN / MAX](#min-max) often used in similar situations.
+
+### Examples
+
+```yql
+SELECT
+ SOME(value)
+FROM my_table;
+```
+
+{% note alert %}
+
+When the aggregate function `SOME` is called multiple times, it's **not** guaranteed that all the resulting values are taken from the same row of the source table. To get this guarantee, pack the values into any container and pass it to `SOME`. For example, in the case of a structure, you can apply [AsStruct](../builtins/basic.md#asstruct)
+
+{% endnote %}
+
+
+
+## CountDistinctEstimate, HyperLogLog, and HLL {#countdistinctestimate}
+
+Approximating the number of unique values using the [HyperLogLog](https://en.wikipedia.org/wiki/HyperLogLog) algorithm. Logically, it does the same thing as [COUNT(DISTINCT ...)](#count), but runs much faster at the cost of some error.
+
+Arguments:
+
+1. Estimated value
+2. Accuracy (4 to 18 inclusive, 14 by default).
+
+By selecting accuracy, you can trade added resource and RAM consumption for decreased error.
+
+All the three functions are aliases at the moment, but `CountDistinctEstimate` may start using a different algorithm in the future.
+
+### Examples
+
+```yql
+SELECT
+ CountDistinctEstimate(my_column)
+FROM my_table;
+```
+
+```yql
+SELECT
+ HyperLogLog(my_column, 4)
+FROM my_table;
+```
+
+
+
+## AGGREGATE_LIST {#agg-list}
+
+Get all column values as a list. When combined with `DISTINCT,` it returns only distinct values. The optional second parameter sets the maximum number of values to be returned. A zero limit value means unlimited.
+
+If you know already that you have few distinct values, use the `AGGREGATE_LIST_DISTINCT` aggregate function to build the same result in memory (that might not be enough for a large number of distinct values).
+
+The order of elements in the result list depends on the implementation and can't be set externally. To return an ordered list, sort the result, for example, with [ListSort](list.md#listsort).
+
+To return a list of multiple values from one line, **DO NOT** use the `AGGREGATE_LIST` function several times, but add all the needed values to a container, for example, via [AsList](basic.md#aslist) or [AsTuple](basic.md#astuple), then pass this container to a single `AGGREGATE_LIST` call.
+
+For example, you can combine it with `DISTINCT` and the function [String::JoinFromList](../udf/list/string.md) (it's an equivalent of `','.join(list)` in Python) to output to a string all the values found in the column after [GROUP BY](../syntax/group_by.md).
+
+### Examples
+
+```yql
+SELECT
+ AGGREGATE_LIST( region ),
+ AGGREGATE_LIST( region, 5 ),
+ AGGREGATE_LIST( DISTINCT region ),
+ AGGREGATE_LIST_DISTINCT( region ),
+ AGGREGATE_LIST_DISTINCT( region, 5 )
+FROM users
+```
+
+```yql
+-- An equivalent of GROUP_CONCAT in MySQL
+SELECT
+ String::JoinFromList(CAST(AGGREGATE_LIST(region, 2) AS List<String>), ",")
+FROM users
+```
+
+These functions also have a short notation: `AGG_LIST` and `AGG_LIST_DISTINCT`.
+
+{% note alert %}
+
+Execution is **NOT** lazy, so when you use it, be sure that the list has a reasonable size (about a thousand items or less). To stay on the safe side, better use a second optional numeric argument that limits the number of items in the list.
+
+{% endnote %}
+
+
+
+## MAX_BY and MIN_BY {#max-min-by}
+
+Return the value of the first argument for the table row where the second argument is minimum/maximum.
+
+You can optionally specify the third argument N that affects behavior if the table has multiple rows with the same minimum or maximum value:
+
+* If N is omitted, the value of one of the rows is returned, and the other rows are discarded.
+* If N is specified, the list is returned with all values, but their number can't exceed N. All values after the number are discarded.
+
+When choosing N, we recommend that you don't exceed several hundreds or thousands to avoid issues with the memory limit.
+
+If your task needs absolutely all values, and their number is measured in dozens of thousands or more, then instead of those aggregate functions better use `JOIN` on the source table with a subquery doing `GROUP BY + MIN/MAX` on the desired columns of this table.
+
+{% note warning "Attention" %}
+
+If the second argument is always `NULL`, the aggregation result is `NULL`.
+
+{% endnote %}
+
+When you use [aggregation factories](basic.md#aggregationfactory), a `Tuple` containing a value and a key is passed as the first [AGGREGATE_BY](#aggregate-by) argument.
+
+### Examples
+
+```yql
+SELECT
+ MIN_BY(value, LENGTH(value)),
+ MAX_BY(value, key, 100)
+FROM my_table;
+```
+
+```yql
+$min_by_factory = AggregationFactory("MIN_BY");
+$max_by_factory = AggregationFactory("MAX_BY", 100);
+
+SELECT
+ AGGREGATE_BY(AsTuple(value, LENGTH(value)), $min_by_factory),
+ AGGREGATE_BY(AsTuple(value, key), $max_by_factory)
+FROM my_table;
+```
+
+
+
+## TOP and BOTTOM {#top-bottom}
+
+Return a list of the maximum/minimum values of an expression. The first argument is an expression, the second argument limits the number of items.
+
+### Examples
+
+```yql
+SELECT
+ TOP(key, 3),
+ BOTTOM(value, 3)
+FROM my_table;
+```
+
+```yql
+$top_factory = AggregationFactory("TOP", 3);
+$bottom_factory = AggregationFactory("BOTTOM", 3);
+
+SELECT
+ AGGREGATE_BY(key, $top_factory),
+ AGGREGATE_BY(value, $bottom_factory)
+FROM my_table;
+```
+
+## TOP_BY and BOTTOM_BY {#top-bottom-by}
+
+Return a list of values of the first argument for the rows containing the maximum/minimum values of the second argument. The third argument limits the number of items in the list.
+
+When you use [aggregation factories](basic.md#aggregationfactory), a `Tuple` containing a value and a key is passed as the first [AGGREGATE_BY](#aggregate-by) argument. In this case, the limit for the number of items is passed by the second argument at factory creation.
+
+### Examples
+
+```yql
+SELECT
+ TOP_BY(value, LENGTH(value), 3),
+ BOTTOM_BY(value, key, 3)
+FROM my_table;
+```
+
+```yql
+$top_by_factory = AggregationFactory("TOP_BY", 3);
+$bottom_by_factory = AggregationFactory("BOTTOM_BY", 3);
+
+SELECT
+ AGGREGATE_BY(AsTuple(value, LENGTH(value)), $top_by_factory),
+ AGGREGATE_BY(AsTuple(value, key), $bottom_by_factory)
+FROM my_table;
+```
+
+
+
+## TOPFREQ and MODE {#topfreq-mode}
+
+Getting an **approximate** list of the most common values in a column with an estimation of their count. Returns a list of structures with two fields:
+
+* `Value`: the frequently occurring value that was found.
+* `Frequency`: An estimated value occurrence in the table.
+
+Required argument: the value itself.
+
+Optional arguments:
+
+1. For `TOPFREQ`, the desired number of items in the result. `MODE` is an alias to `TOPFREQ` with this argument set to 1. For `TOPFREQ`, this argument is also 1 by default.
+2. The number of items in the buffer used: lets you trade memory consumption for accuracy. Default: 100.
+
+### Examples
+
+```yql
+SELECT
+ MODE(my_column),
+ TOPFREQ(my_column, 5, 1000)
+FROM my_table;
+```
+
+
+
+## STDDEV and VARIANCE {#stddev-variance}
+
+Standard deviation and variance in a column. Those functions use a [single-pass parallel algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Parallel_algorithm), whose result may differ from the more common methods requiring two passes through the data.
+
+By default, the sample variance and standard deviation are calculated. Several write methods are available:
+
+* with the `POPULATION` suffix/prefix, for example: `VARIANCE_POPULATION`, `POPULATION_VARIANCE` calculates the variance or standard deviation for the population.
+* With the `SAMPLE` suffix/prefix or without a suffix, for example, `VARIANCE_SAMPLE`, `SAMPLE_VARIANCE`, `SAMPLE` calculate sample variance and standard deviation.
+
+Several abbreviated aliases are also defined, for example, `VARPOP` or `STDDEVSAMP`.
+
+If all the values passed are `NULL`, it returns `NULL`.
+
+### Examples
+
+```yql
+SELECT
+ STDDEV(numeric_column),
+ VARIANCE(numeric_column)
+FROM my_table;
+```
+
+
+
+## CORRELATION and COVARIANCE {#correlation-covariance}
+
+Correlation and covariance between two columns.
+
+Abbreviated versions are also available: `CORR` or `COVAR`. For covariance, there are also versions with the `SAMPLE`/`POPULATION` suffix that are similar to [VARIANCE](#stddev-variance) above.
+
+Unlike most other aggregate functions, they don't skip `NULL`, but accept it as 0.
+
+When you use [aggregation factories](basic.md#aggregationfactory), a `Tuple` containing two values is passed as the first [AGGREGATE_BY](#aggregate-by) argument.
+
+### Examples
+
+```yql
+SELECT
+ CORRELATION(numeric_column, another_numeric_column),
+ COVARIANCE(numeric_column, another_numeric_column)
+FROM my_table;
+```
+
+```yql
+$corr_factory = AggregationFactory("CORRELATION");
+
+SELECT
+ AGGREGATE_BY(AsTuple(numeric_column, another_numeric_column), $corr_factory)
+FROM my_table;
+```
+
+
+
+## PERCENTILE and MEDIAN {#percentile-median}
+
+Calculating percentiles using the amortized version of the [TDigest](https://github.com/tdunning/t-digest) algorithm. `MEDIAN`: An alias for `PERCENTILE(N, 0.5)`.
+
+{% note info "Restriction" %}
+
+The first argument (N) must be a table column name. If you need to bypass this restriction, use a subquery. The restriction is introduced to simplify calculations, since the implementation merges the calls with the same first argument (N) into a single pass.
+
+{% endnote %}
+
+```yql
+SELECT
+ MEDIAN(numeric_column),
+ PERCENTILE(numeric_column, 0.99)
+FROM my_table;
+```
+
+
+
+## HISTOGRAM {#histogram}
+
+Plotting an approximate histogram based on a numeric expression with automatic selection of buckets.
+
+[Auxiliary functions](../udf/list/histogram.md)
+
+### Basic settings
+
+You can limit the number of buckets using an optional argument. The default value is 100. Keep in mind that added accuracy costs you more computing resources and may negatively affect the query execution time. In extreme cases, it may affect your query success.
+
+### Support for weights
+
+You can specify a "weight" for each value used in the histogram. To do this, pass to the aggregate function the second argument with an expression for calculating the weight. The weight of `1.0` is always used by default. If you use non-standard weights, you may also use the third argument to limit the number of buckets.
+
+If you pass two arguments, the meaning of the second argument is determined by its type (if it's an integer literal, it limits the number of buckets, otherwise it's used as a weight).
+
+### Algorithms
+
+* [Source whitepaper](http://jmlr.org/papers/volume11/ben-haim10a/ben-haim10a.pdf);
+
+Various modifications of the algorithm are available:
+
+```yql
+AdaptiveDistanceHistogram
+AdaptiveWeightHistogram
+AdaptiveWardHistogram
+BlockWeightHistogram
+BlockWardHistogram
+```
+
+By default, `HISTOGRAM` is a synonym for `AdaptiveWardHistogram`. Both functions are equivalent and interchangeable in all contexts.
+
+The Distance, Weight, and Ward algorithms differ in the formulas that combine two points into one:
+
+```c++
+ TWeightedValue CalcDistanceQuality(const TWeightedValue& left, const TWeightedValue& right) {
+ return TWeightedValue(right.first - left.first, left.first);
+ }
+
+ TWeightedValue CalcWeightQuality(const TWeightedValue& left, const TWeightedValue& right) {
+ return TWeightedValue(right.second + left.second, left.first);
+ }
+
+ TWeightedValue CalcWardQuality(const TWeightedValue& left, const TWeightedValue& right) {
+ const double N1 = left.second;
+ const double N2 = right.second;
+ const double mu1 = left.first;
+ const double mu2 = right.first;
+ return TWeightedValue(N1 * N2 / (N1 + N2) * (mu1 - mu2) * (mu1 - mu2), left.first);
+ }
+```
+
+Difference between Adaptive and Block:
+
+{% block info %}
+
+Contrary to adaptive histogram, block histogram doesn't rebuild bins after each point is added. Instead, it accumulates points and if the amount of points overflows specified limits, it shrinks all the points at once to produce a histogram. Indeed, there exist two limits and two shrinkage operations:
+
+1. FastGreedyShrink is fast but coarse. It is used to shrink from upper limit to intermediate limit (override FastGreedyShrink to set specific behaviour).
+2. SlowShrink is slow, but produces finer histogram. It shrinks from the intermediate limit to the actual number of bins in a manner similar to that in adaptive histogram (set CalcQuality in 34constuctor)
+While FastGreedyShrink is used most of the time, SlowShrink is mostly used for histogram finalization
+
+{% endblock %}
+
+### If you need an accurate histogram
+
+1. You can use the aggregate functions described below with fixed bucket grids: [LinearHistogram](#linearhistogram) or [LogarithmicHistogram](#linearhistogram).
+2. You can calculate the bucket number for each row and apply to it [GROUP BY](../syntax/group_by.md).
+
+When you use [aggregation factories](basic.md#aggregationfactory), a `Tuple` containing a value and a weight is passed as the first [AGGREGATE_BY](#aggregate-by) argument.
+
+### Examples
+
+```yql
+SELECT
+ HISTOGRAM(numeric_column)
+FROM my_table;
+```
+
+```yql
+SELECT
+ Histogram::Print(
+ HISTOGRAM(numeric_column, 10),
+ 50
+ )
+FROM my_table;
+```
+
+```yql
+$hist_factory = AggregationFactory("HISTOGRAM");
+
+SELECT
+ AGGREGATE_BY(AsTuple(numeric_column, 1.0), $hist_factory)
+FROM my_table;
+```
+
+## LinearHistogram, LogarithmicHistogram, and LogHistogram {#linearhistogram}
+
+Plotting a histogram based on an explicitly specified fixed bucket scale.
+
+Arguments:
+
+1. Expression used to plot the histogram. All the following arguments are optional.
+2. Spacing between the `LinearHistogram` buckets or the logarithm base for `LogarithmicHistogram`/`LogHistogram` (those are aliases). In both cases, the default value is 10.
+3. Minimum value. By default, it's minus infinity.
+4. Maximum value. By default, it's plus infinity.
+
+The format of the result is totally similar to [adaptive histograms](#histogram), so you can use the same [set of auxiliary functions](../udf/list/histogram.md).
+
+If the spread of input values is uncontrollably large, we recommend that you specify the minimum and maximum values to prevent potential failures due to high memory consumption.
+
+### Examples
+
+```yql
+SELECT
+ LogarithmicHistogram(numeric_column, 2)
+FROM my_table;
+```
+
+
+
+## BOOL_AND, BOOL_OR and BOOL_XOR {#bool-and-or-xor}
+
+### Signature
+
+```yql
+BOOL_AND(Bool?)->Bool?
+BOOL_OR(Bool?)->Bool?
+BOOL_XOR(Bool?)->Bool?
+```
+
+Apply the relevant logical operation (`AND`/`OR`/`XOR`) to all values in a Boolean column or expression.
+
+Unlike most other aggregate functions, these functions **don't skip** `NULL` during aggregation and use the following rules:
+
+- `true AND null == null`
+- `false OR null == null`
+
+For `BOOL_AND`:
+
+- If at least one `NULL` value is present, the result is `NULL` regardless of `true` values in the expression.
+- If at least one `false` value is present, the result changes to `false` regardless of `NULL` values in the expression.
+
+For `BOOL_OR`:
+
+- If at least one `NULL` value is present, the result changes to `NULL` regardless of `false` values in the expression.
+- If at least one `true` value is present, the result changes to `true` regardless of `NULL` values in the expression.
+
+For `BOOL_XOR`:
+
+- The result is `NULL` if any `NULL` is found.
+
+Examples of such behavior can be found below.
+
+To skip `NULL` values during aggregation, use the `MIN`/`MAX` or `BIT_AND`/`BIT_OR`/`BIT_XOR` functions.
+
+### Examples
+
+```yql
+$data = [
+ <|nonNull: true, nonFalse: true, nonTrue: NULL, anyVal: true|>,
+ <|nonNull: false, nonFalse: NULL, nonTrue: NULL, anyVal: NULL|>,
+ <|nonNull: false, nonFalse: NULL, nonTrue: false, anyVal: false|>,
+];
+
+SELECT
+ BOOL_AND(nonNull) as nonNullAnd, -- false
+ BOOL_AND(nonFalse) as nonFalseAnd, -- NULL
+ BOOL_AND(nonTrue) as nonTrueAnd, -- false
+ BOOL_AND(anyVal) as anyAnd, -- false
+ BOOL_OR(nonNull) as nonNullOr, -- true
+ BOOL_OR(nonFalse) as nonFalseOr, -- true
+ BOOL_OR(nonTrue) as nonTrueOr, -- NULL
+ BOOL_OR(anyVal) as anyOr, -- true
+ BOOL_XOR(nonNull) as nonNullXor, -- true
+ BOOL_XOR(nonFalse) as nonFalseXor, -- NULL
+ BOOL_XOR(nonTrue) as nonTrueXor, -- NULL
+ BOOL_XOR(anyVal) as anyXor, -- NULL
+FROM AS_TABLE($data);
+```
+
+## BIT_AND, BIT_OR and BIT_XOR {#bit-and-or-xor}
+
+Apply the relevant bitwise operation to all values of a numeric column or expression.
+
+### Examples
+
+```yql
+SELECT
+ BIT_XOR(unsigned_numeric_value)
+FROM my_table;
+```
+
+## SessionStart {#session-start}
+
+No arguments. It's allowed only if there is [SessionWindow](../syntax/group_by.md#session-window) in [GROUP BY](../syntax/group_by.md) / [PARTITION BY](../syntax/window.md#partition).
+Returns the value of the `SessionWindow` key column. If `SessionWindow` has two arguments, it returns the minimum value of the first argument within the group/section.
+In the case of the expanded version `SessionWindow`, it returns the value of the second element from the tuple returned by `<calculate_lambda>`, for which the first tuple element is `True`.
+
+
+## AGGREGATE_BY and MULTI_AGGREGATE_BY {#aggregate-by}
+
+Applying an [aggregation factory](basic.md#aggregationfactory) to all values of a column or expression. The `MULTI_AGGREGATE_BY` function requires that the value of a column or expression has a structure, tuple, or list, and applies the factory to each individual element, placing the result in a container of the same format. If different values of a column or expression contain lists of different length, the resulting list will have the smallest of the source lengths.
+
+1. Column, `DISTINCT` column or expression.
+2. Factory.
+
+### Examples
+
+```yql
+$count_factory = AggregationFactory("COUNT");
+
+SELECT
+ AGGREGATE_BY(DISTINCT column, $count_factory) as uniq_count
+FROM my_table;
+
+SELECT
+ MULTI_AGGREGATE_BY(nums, AggregationFactory("count")) as count,
+ MULTI_AGGREGATE_BY(nums, AggregationFactory("min")) as min,
+ MULTI_AGGREGATE_BY(nums, AggregationFactory("max")) as max,
+ MULTI_AGGREGATE_BY(nums, AggregationFactory("avg")) as avg,
+ MULTI_AGGREGATE_BY(nums, AggregationFactory("percentile", 0.9)) as p90
+FROM my_table;
+```
+
+
diff --git a/yql/essentials/docs/en/builtins/basic.md b/yql/essentials/docs/en/builtins/basic.md
new file mode 100644
index 00000000000..dfd3adb0497
--- /dev/null
+++ b/yql/essentials/docs/en/builtins/basic.md
@@ -0,0 +1,1213 @@
+
+# Basic built-in functions
+
+Below are the general-purpose functions. For specialized functions, there are separate articles: [aggregate functions](aggregation.md), [window functions](window.md), and functions for [lists](list.md), [dictionaries](dict.md), [structures](struct.md), [data types](types.md), and [code generation](codegen.md).
+
+
+
+## COALESCE {#coalesce}
+
+Iterates through the arguments from left to right and returns the first non-empty argument found. To be sure that the result is non-empty (not of an [optional type](../types/optional.md)), the rightmost argument must be of this type (often a literal is used for this). With a single argument, returns this argument unchanged.
+
+Lets you pass potentially empty values to functions that can't handle them by themselves.
+
+A short format using the low-priority `??` operator is available (lower than the Boolean operations). You can use the `NVL` alias.
+
+### Examples
+
+```yql
+SELECT COALESCE(
+ maybe_empty_column,
+ "it's empty!"
+) FROM my_table;
+```
+
+```yql
+SELECT
+ maybe_empty_column ?? "it's empty!"
+FROM my_table;
+```
+
+```yql
+SELECT NVL(
+ maybe_empty_column,
+ "it's empty!"
+) FROM my_table;
+```
+
+All three examples above are equivalent.
+
+
+
+## LENGTH {#length}
+
+Returns the length of the string in bytes. This function is also available under the `LEN` name .
+
+### Examples
+
+```yql
+SELECT LENGTH("foo");
+```
+
+```yql
+SELECT LEN("bar");
+```
+
+{% note info %}
+
+To calculate the length of a string in Unicode characters, you can use the function [Unicode::GetLength](../udf/list/unicode.md).<br/><br/>To get the number of elements in the list, use the function [ListLength](list.md#listlength).
+
+{% endnote %}
+
+
+## SUBSTRING {#substring}
+
+Returns a substring.
+
+Required arguments:
+
+* Source string;
+* Position: The offset from the beginning of the string in bytes (integer) or `NULL` meaning "from the beginning".
+
+Optional arguments:
+
+* Substring length: The number of bytes starting from the specified position (an integer, or the default `NULL` meaning "up to the end of the source string").
+
+Indexing starts from zero. If the specified position and length are beyond the string, returns an empty string.
+If the input string is optional, the result is also optional.
+
+### Examples
+
+```yql
+SELECT SUBSTRING("abcdefg", 3, 1); -- d
+```
+
+```yql
+SELECT SUBSTRING("abcdefg", 3); -- defg
+```
+
+```yql
+SELECT SUBSTRING("abcdefg", NULL, 3); -- abc
+```
+
+
+
+## FIND {#find}
+
+Finding the position of a substring in a string.
+
+Required arguments:
+
+* Source string;
+* The substring being searched for.
+
+Optional arguments:
+
+* A position in bytes to start the search with (an integer or `NULL` by default that means "from the beginning of the source string").
+
+Returns the first substring position found or `NULL` (meaning that the desired substring hasn't been found starting from the specified position).
+
+### Examples
+
+```yql
+SELECT FIND("abcdefg_abcdefg", "abc"); -- 0
+```
+
+```yql
+SELECT FIND("abcdefg_abcdefg", "abc", 1); -- 8
+```
+
+```yql
+SELECT FIND("abcdefg_abcdefg", "abc", 9); -- null
+```
+
+## RFIND {#rfind}
+
+Reverse finding the position of a substring in a string, from the end to the beginning.
+
+Required arguments:
+
+* Source string;
+* The substring being searched for.
+
+Optional arguments:
+
+* A position in bytes to start the search with (an integer or `NULL` by default, meaning "from the end of the source string").
+
+Returns the first substring position found or `NULL` (meaning that the desired substring hasn't been found starting from the specified position).
+
+### Examples
+
+```yql
+SELECT RFIND("abcdefg_abcdefg", "bcd"); -- 9
+```
+
+```yql
+SELECT RFIND("abcdefg_abcdefg", "bcd", 8); -- 1
+```
+
+```yql
+SELECT RFIND("abcdefg_abcdefg", "bcd", 0); -- null
+```
+
+
+
+## StartsWith, EndsWith {#starts_ends_with}
+
+Checking for a prefix or suffix in a string.
+
+Required arguments:
+
+* Source string;
+* The substring being searched for.
+
+The arguments can be of the `String` or `Utf8` type and can be optional.
+
+### Examples
+
+```yql
+SELECT StartsWith("abc_efg", "abc") AND EndsWith("abc_efg", "efg"); -- true
+```
+
+```yql
+SELECT StartsWith("abc_efg", "efg") OR EndsWith("abc_efg", "abc"); -- false
+```
+
+```yql
+SELECT StartsWith("abcd", NULL); -- null
+```
+
+```yql
+SELECT EndsWith(NULL, Utf8("")); -- null
+```
+
+
+
+## IF {#if}
+
+Checks the condition: `IF(condition_expression, then_expression, else_expression)`.
+
+It's a simplified alternative for [CASE WHEN ... THEN ... ELSE ... END](../syntax/expressions.md#case).
+
+You may omit the `else_expression` argument. In this case, if the condition is false (`condition_expression` returned `false`), an empty value is returned with the type corresponding to `then_expression` and allowing for `NULL`. Hence, the result will have an [optional data type](../types/optional.md).
+
+### Examples
+
+```yql
+SELECT
+ IF(foo > 0, bar, baz) AS bar_or_baz,
+ IF(foo > 0, foo) AS only_positive_foo
+FROM my_table;
+```
+
+
+
+## NANVL {#nanvl}
+
+Replaces the values of `NaN` (not a number) in expressions like `Float`, `Double`, or [Optional](../types/optional.md).
+
+Arguments:
+
+1. The expression where you want to make a replacement.
+2. The value to replace `NaN`.
+
+If one of the arguments is `Double`, the result is`Double`, otherwise, it's `Float`. If one of the arguments is `Optional`, then the result is `Optional`.
+
+### Examples
+
+```yql
+SELECT
+ NANVL(double_column, 0.0)
+FROM my_table;
+```
+
+
+
+## Random... {#random}
+
+Generates a pseudorandom number:
+
+* `Random()`: A floating point number (Double) from 0 to 1.
+* `RandomNumber()`: An integer from the complete Uint64 range.
+* `RandomUuid()`: [Uuid version 4](https://tools.ietf.org/html/rfc4122#section-4.4).
+
+### Signatures
+
+```yql
+Random(T1[, T2, ...])->Double
+RandomNumber(T1[, T2, ...])->Uint64
+RandomUuid(T1[, T2, ...])->Uuid
+```
+
+No arguments are used for random number generation: they are only needed to control the time of the call. A new random number is returned at each call. Therefore:
+
+* If Random is called again within a **same query** and with a same set of arguments, the same set of random numbers is returned. Keep in mind that we mean the arguments themselves (i.e., the text between parentheses) rather than their values.
+
+* Calling of Random with the same set of arguments in **different queries** returns different sets of random numbers.
+
+{% note warning %}
+
+If Random is used in [named expressions](../syntax/expressions.md#named-nodes), its one-time calculation is not guaranteed. Depending on the optimizers and runtime environment, it can be counted both once and multiple times. To make sure it's only counted once, materialize a named expression into a table.
+
+{% endnote %}
+
+Use cases:
+
+* `SELECT RANDOM(1);`: Get one random value for the entire query and use it multiple times (to get multiple random values, you can pass various constants of any type).
+* `SELECT RANDOM(1) FROM table;`: The same random number for each row in the table.
+* `SELECT RANDOM(1), RANDOM(2) FROM table;`: Two random numbers for each row of the table, all the numbers in each of the columns are the same.
+* `SELECT RANDOM(some_column) FROM table;`: Different random numbers for each row in the table.
+* `SELECT RANDOM(some_column), RANDOM(some_column) FROM table;`: Different random numbers for each row of the table, but two identical numbers within the same row.
+* `SELECT RANDOM(some_column), RANDOM(some_column + 1) FROM table;` or `SELECT RANDOM(some_column), RANDOM(other_column) FROM table;`: Two columns, with different numbers in both.
+
+### Examples
+
+```yql
+SELECT
+ Random(key) -- [0, 1)
+FROM my_table;
+```
+
+```yql
+SELECT
+ RandomNumber(key) -- [0, Max<Uint64>)
+FROM my_table;
+```
+
+```yql
+SELECT
+ RandomUuid(key) -- Uuid version 4
+FROM my_table;
+```
+
+```yql
+SELECT
+ RANDOM(column) AS rand1,
+ RANDOM(column) AS rand2, -- same as rand1
+ RANDOM(column, 1) AS randAnd1, -- different from rand1/2
+ RANDOM(column, 2) AS randAnd2 -- different from randAnd1
+FROM my_table;
+```
+
+
+## CurrentUtc... {#current-utc}
+
+`CurrentUtcDate()`, `CurrentUtcDatetime()` and `CurrentUtcTimestamp()`: Getting the current date and/or time in UTC. The result data type is specified at the end of the function name.
+
+The arguments are optional and work same as [RANDOM](#random).
+
+### Examples
+
+```yql
+SELECT CurrentUtcDate();
+```
+
+```yql
+SELECT CurrentUtcTimestamp(TableRow()) FROM my_table;
+```
+
+
+
+## CurrentTz... {#current-tz}
+
+`CurrentTzDate()`, `CurrentTzDatetime()`, and `CurrentTzTimestamp()`: Get the current date and/or time in the [IANA time zone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) specified in the first argument. The result data type is specified at the end of the function name.
+
+The arguments that follow are optional and work same as [RANDOM](#random).
+
+### Examples
+
+```yql
+SELECT CurrentTzDate("Europe/Moscow");
+```
+
+```yql
+SELECT CurrentTzTimestamp("Europe/Moscow", TableRow()) FROM my_table;
+```
+
+## AddTimezone
+
+Adding the time zone information to the date/time in UTC. In the result of `SELECT` or after `CAST`, a `String` will be subject to the time zone rules used to calculate the time offset.
+
+Arguments:
+
+1. Date: the type is `Date`/`Datetime`/`Timestamp`.
+2. [The IANA name of the time zone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
+
+Result type: `TzDate`/`TzDatetime`/`TzTimestamp`, depending on the input data type.
+
+### Examples
+
+```yql
+SELECT AddTimezone(Datetime("2018-02-01T12:00:00Z"), "Europe/Moscow");
+```
+
+## RemoveTimezone
+
+Removing the time zone data and converting the value to date/time in UTC.
+
+Arguments:
+
+1. Date: the type is `TzDate`/`TzDatetime`/`TzTimestamp`.
+
+Result type: `Date`/`Datetime`/`Timestamp`, depending on the input data type.
+
+### Examples
+
+```yql
+SELECT RemoveTimezone(TzDatetime("2018-02-01T12:00:00,Europe/Moscow"));
+```
+
+
+
+## Version {#version}
+
+`Version()` returns a string describing the current version of the node processing the request. In some cases, such as during rolling upgrades, it might return different strings depending on which node processes the request. It does not accept any arguments.
+
+### Examples
+
+```yql
+SELECT Version();
+```
+
+
+
+## MAX_OF, MIN_OF, GREATEST, and LEAST {#max-min}
+
+Returns the minimum or maximum among N arguments. Those functions let you replace the SQL standard statement `CASE WHEN a < b THEN a ELSE b END` that would be too sophisticated for N more than two.
+
+The argument types must be mutually castable and accept `NULL`.
+
+`GREATEST` is a synonym for `MAX_OF` and `LEAST` is a synonym for `MIN_OF`.
+
+### Examples
+
+```yql
+SELECT MIN_OF(1, 2, 3);
+```
+
+
+
+## AsTuple, AsStruct, AsList, AsDict, AsSet, AsListStrict, AsDictStrict and AsSetStrict {#as-container}
+
+Creates containers of the applicable types. For container literals, [operator notation](#containerliteral) is also supported.
+
+Specifics:
+
+* The container elements are passed in arguments. Hence, the number of elements in the resulting container is equal to the number of arguments passed, except when the dictionary keys repeat.
+* `AsTuple` and `AsStruct` can be called without arguments, and also the arguments can have different types.
+* The field names in `AsStruct` are set using `AsStruct(field_value AS field_name)`.
+* Creating a list requires at least one argument if you need to output the element types. To create an empty list with the given type of elements, use the function [ListCreate](list.md#listcreate). You can create an empty list as an `AsList()` call without arguments. In this case, this expression will have the `EmptyList` type.
+* Creating a dictionary requires at least one argument if you need to output the element types. To create an empty dictionary with the given type of elements, use the function [DictCreate](dict.md#dictcreate). You can create an empty dictionary as an `AsDict()` call without arguments, in this case, this expression will have the `EmptyDict` type.
+* Creating a set requires at least one argument if you need to output element types. To create an empty set with the given type of elements, use the function [SetCreate](dict.md#setcreate). You can create an empty set as an `AsSet()` call without arguments, in this case, this expression will have the `EmptySet` type.
+* `AsList` outputs the common type of elements in the list. A type error is raised in the case of incompatible types.
+* `AsDict` separately outputs the common types for keys and values. A type error is raised in the case of incompatible types.
+* `AsSet` outputs common types for keys. A type error is raised in the case of incompatible types.
+* `AsListStrict`, `AsDictStrict`, `AsSetStrict` require the same type for their arguments.
+* `AsDict` and `AsDictStrict` expect `Tuple` of two elements as arguments (key and value, respectively). If the keys repeat, only the value for the first key remains in the dictionary.
+* `AsSet` and `AsSetStrict` expect keys as arguments.
+
+### Examples
+
+```yql
+SELECT
+ AsTuple(1, 2, "3") AS `tuple`,
+ AsStruct(
+ 1 AS a,
+ 2 AS b,
+ "3" AS c
+ ) AS `struct`,
+ AsList(1, 2, 3) AS `list`,
+ AsDict(
+ AsTuple("a", 1),
+ AsTuple("b", 2),
+ AsTuple("c", 3)
+ ) AS `dict`,
+ AsSet(1, 2, 3) AS `set`
+```
+
+
+
+## Container literals {#containerliteral}
+
+Some containers support operator notation for their literal values:
+
+* Tuple: `(value1, value2...)`;
+* Structure: `<|name1: value1, name2: value2...|>`;
+* List: `[value1, value2,...]`;
+* Dictionary: `{key1: value1, key2: value2...}`;
+* Set: `{key1, key2...}`.
+
+In every case, you can use an insignificant trailing comma. For a tuple with one element, this comma is required: `(value1,)`.
+For field names in the structure literal, you can use an expression that can be calculated at evaluation time, for example, string literals or identifiers (including those enclosed in backticks).
+
+For nested lists, use [AsList](#as-container), for nested dictionaries, use [AsDict](#as-container), for nested sets, use [AsSet](#as-container), for nested tuples, use [AsTuple](#as-container), for nested structures, use [AsStruct](#as-container).
+
+### Examples
+
+```yql
+$name = "computed " || "member name";
+SELECT
+ (1, 2, "3") AS `tuple`,
+ <|
+ `complex member name`: 2.3,
+ b: 2,
+ $name: "3",
+ "inline " || "computed member name": false
+ |> AS `struct`,
+ [1, 2, 3] AS `list`,
+ {
+ "a": 1,
+ "b": 2,
+ "c": 3,
+ } AS `dict`,
+ {1, 2, 3} AS `set`
+```
+
+
+
+## Variant {#variant}
+
+`Variant()` creates a variant value over a tuple or structure.
+
+Arguments:
+
+* Value
+* String with a field name or tuple index
+* Variant type
+
+### Example
+
+```yql
+$var_type = Variant<foo: Int32, bar: Bool>;
+
+SELECT
+ Variant(6, "foo", $var_type) as Variant1Value,
+ Variant(false, "bar", $var_type) as Variant2Value;
+```
+
+## AsVariant {#asvariant}
+
+`AsVariant()` creates a value of a [variant over a structure](../types/containers.md) including one field. This value can be implicitly converted to any variant over a structure that has a matching data type for this field name and might include more fields with other names.
+
+Arguments:
+
+* Value
+* A string with the field name
+
+### Example
+
+```yql
+SELECT
+ AsVariant(6, "foo") as VariantValue
+```
+
+## Visit, VisitOrDefault {#visit}
+
+Processes the possible values of a variant over a structure or tuple using the provided handler functions for each field/element of the variant.
+
+### Signature
+
+```yql
+Visit(Variant<key1: K1, key2: K2, ...>, K1->R AS key1, K2->R AS key2, ...)->R
+Visit(Variant<K1, K2, ...>, K1->R, K2->R, ...)->R
+
+VisitOrDefault(Variant<K1, K2, ...>{Flags:AutoMap}, R, [K1->R, [K2->R, ...]])->R
+VisitOrDefault(Variant<key1: K1, key2: K2, ...>{Flags:AutoMap}, R, [K1->R AS key1, [K2->R AS key2, ...]])->R
+```
+
+### Arguments
+
+* For a variant over structure: accepts the variant as the positional argument and named arguments (handlers) corresponding to each field of the variant.
+* For a variant over tuple: accepts the variant and handlers for each element of the variant as positional arguments.
+* `VisitOrDefault` includes an additional positional argument (on the second place) for the default value, enabling the omission of certain handlers.
+
+### Example
+
+```yql
+$vartype = Variant<num: Int32, flag: Bool, str: String>;
+$handle_num = ($x) -> { return 2 * $x; };
+$handle_flag = ($x) -> { return If($x, 200, 10); };
+$handle_str = ($x) -> { return Unwrap(CAST(LENGTH($x) AS Int32)); };
+
+$visitor = ($var) -> { return Visit($var, $handle_num AS num, $handle_flag AS flag, $handle_str AS str); };
+SELECT
+ $visitor(Variant(5, "num", $vartype)), -- 10
+ $visitor(Just(Variant(True, "flag", $vartype))), -- Just(200)
+ $visitor(Just(Variant("somestr", "str", $vartype))), -- Just(7)
+ $visitor(Nothing(OptionalType($vartype))), -- Nothing(Optional<Int32>)
+ $visitor(NULL) -- NULL
+;
+```
+
+## VariantItem {#variantitem}
+
+Returns the value of a homogeneous variant (i.e., a variant containing fields/elements of the same type).
+
+### Signature
+
+```yql
+VariantItem(Variant<key1: K, key2: K, ...>{Flags:AutoMap})->K
+VariantItem(Variant<K, K, ...>{Flags:AutoMap})->K
+```
+
+### Example
+
+```yql
+$vartype1 = Variant<num1: Int32, num2: Int32, num3: Int32>;
+SELECT
+ VariantItem(Variant(7, "num2", $vartype1)), -- 7
+ VariantItem(Just(Variant(5, "num1", $vartype1))), -- Just(5)
+ VariantItem(Nothing(OptionalType($vartype1))), -- Nothing(Optional<Int32>)
+ VariantItem(NULL) -- NULL
+;
+```
+
+## Way {#way}
+
+Returns an active field (active index) of a variant over a struct (tuple).
+
+### Signature
+
+```yql
+Way(Variant<key1: K1, key2: K2, ...>{Flags:AutoMap})->Utf8
+Way(Variant<K1, K2, ...>{Flags:AutoMap})->Uint32
+```
+
+### Example
+
+```yql
+$vr = Variant(1, "0", Variant<Int32, String>);
+$vrs = Variant(1, "a", Variant<a:Int32, b:String>);
+SELECT Way($vr); -- 0
+SELECT Way($vrs); -- "a"
+```
+
+
+## Enum {#enum}
+
+`Enum()` creates an enumeration value.
+
+Arguments:
+
+* A string with the field name
+* Enumeration type
+
+### Example
+
+```yql
+$enum_type = Enum<Foo, Bar>;
+SELECT
+ Enum("Foo", $enum_type) as Enum1Value,
+ Enum("Bar", $enum_type) as Enum2Value;
+```
+
+## AsEnum {#asenum}
+
+`AsEnum()` creates a value of [enumeration](../types/containers.md) including one element. This value can be implicitly cast to any enumeration containing such a name.
+
+Arguments:
+
+* A string with the name of an enumeration item
+
+### Example
+
+```yql
+SELECT
+ AsEnum("Foo");
+```
+
+
+
+## AsTagged, Untag {#as-tagged}
+
+Wraps the value in the [Tagged data type](../types/special.md) with the specified tag, preserving the physical data type. `Untag`: The reverse operation.
+
+Required arguments:
+
+1. Value of any type.
+2. Tag name.
+
+Returns a copy of the value from the first argument with the specified tag in the data type.
+
+Examples of use cases:
+
+* Returns to the client's web interface the media files from BASE64-encoded strings.
+* Prevent passing of invalid values at the boundaries of UDF calls.
+* Additional refinements at the level of returned columns types.
+
+## TablePath {#tablepath}
+
+Access to the current table name, which might be needed when you use [CONCAT](../syntax/select/concat.md#concat), and other related functions.
+
+No arguments. Returns a string with the full path or an empty string and warning when used in an unsupported context (for example, when working with a subquery or a range of 1000+ tables).
+
+{% note info %}
+
+The [TablePath](#tablepath), [TableName](#tablename), and [TableRecordIndex](#tablerecordindex) functions don't support temporary and anonymous tables (they return an empty string or 0 for [TableRecordIndex](#tablerecordindex)).
+These functions are calculated when [executing](../syntax/select/index.md#selectexec) projections in `SELECT`, and by that time the current table may already be temporary.
+To avoid such a situation, create a subquery for calculating these functions, as shown in the second example below.
+
+{% endnote %}
+
+### Examples
+
+```yql
+SELECT TablePath() FROM CONCAT(table_a, table_b);
+```
+
+```yql
+SELECT key, tpath_ AS path FROM (SELECT a.*, TablePath() AS tpath_ FROM RANGE(`my_folder`) AS a)
+WHERE key IN $subquery;
+```
+
+## TableName {#tablename}
+
+Get the table name based on the table path. You can obtain the path using the [TablePath](#tablepath) function.
+
+Optional arguments:
+
+* Path to the table, `TablePath()` is used by default (see also its limitations).
+* Specifying the system ("yt") whose rules are used to determine the table name. You need to specify the system only if [USE](../syntax/use.md) doesn't specify the current cluster.
+
+### Examples
+
+```yql
+USE cluster;
+SELECT TableName() FROM CONCAT(table_a, table_b);
+```
+
+
+## TableRecordIndex {#tablerecordindex}
+
+Access to the current sequence number of a row in the physical source table, **starting from 1** (depends on the storage implementation).
+
+No arguments. When used in combination with [CONCAT](../syntax/select/concat.md#concat), and other similar mechanisms, numbering restarts for each input table. If used in an incorrect context, it returns 0.
+
+### Example
+
+```yql
+SELECT TableRecordIndex() FROM my_table;
+```
+
+## TableRow, JoinTableRow {#tablerow}
+
+Getting the entire table row as a structure. No arguments. `JoinTableRow` in case of `JOIN` always returns a structure with table prefixes.
+
+### Example
+
+```yql
+SELECT TableRow() FROM my_table;
+```
+
+## FileContent and FilePath {#file-content-path}
+
+The `FileContent` and `FilePath` argument is a string with an alias.
+
+### Examples
+
+```yql
+SELECT "Content of "
+ || FilePath("my_file.txt")
+ || ":\n"
+ || FileContent("my_file.txt");
+```
+
+## FolderPath {#folderpath}
+
+Getting the path to the root of a directory with several "attached" files with the common prefix specified.
+
+The argument is a string with a prefix among aliases.
+
+See also [PRAGMA File](../syntax/pragma.md#file) and [PRAGMA Folder](../syntax/pragma.md#folder).
+
+### Examples
+
+```yql
+PRAGMA File("foo/1.txt", "http://url/to/somewhere");
+PRAGMA File("foo/2.txt", "http://url/to/somewhere/else");
+PRAGMA File("bar/3.txt", "http://url/to/some/other/place");
+
+SELECT FolderPath("foo"); -- The directory at the return path will
+ -- include the files 1.txt and 2.txt downloaded from the above links
+```
+
+## ParseFile
+
+Get a list of values from the attached text file. It can be combined with [IN](../syntax/expressions.md#in), attaching the file by URL.
+
+Only one file format is supported: one value per line.
+
+Two required arguments:
+
+1. List cell type: only strings and numeric types are supported.
+2. The name of the attached file.
+
+{% note info %}
+
+The return value is a lazy list. For repeat use, wrap it in the function [ListCollect](list.md#listcollect)
+
+{% endnote %}
+
+### Examples
+
+```yql
+SELECT ListLength(ParseFile("String", "my_file.txt"));
+```
+
+```yql
+SELECT * FROM my_table
+WHERE int_column IN ParseFile("Int64", "my_file.txt");
+```
+
+## Ensure... {#ensure}
+
+Checking for the user conditions:
+
+* `Ensure()`: Checking whether the predicate is true at query execution.
+* `EnsureType()`: Checking that the expression type exactly matches the specified type.
+* `EnsureConvertibleTo()`: A soft check of the expression type (with the same rules as for implicit type conversion).
+
+If the check fails, the entire query fails.
+
+Arguments:
+
+1. An expression that will result from a function call if the check is successful. It's also checked for the data type in the corresponding functions.
+2. Ensure uses a Boolean predicate that is checked for being `true`. The other functions use the data type that can be obtained using the [relevant functions](types.md), or a string literal with a [text description of the type](../types/type_string.md).
+3. An optional string with an error comment to be included in the overall error message when the query is complete. The data itself can't be used for type checks, since the data check is performed at query validation (or can be an arbitrary expression in the case of Ensure).
+
+To check the conditions based on the final calculation result, it's convenient to combine Ensure with [DISCARD SELECT](../syntax/discard.md).
+
+### Examples
+
+```yql
+SELECT Ensure(
+ value,
+ value < 100,
+ "value out or range"
+) AS value FROM my_table;
+```
+
+```yql
+SELECT EnsureType(
+ value,
+ TypeOf(other_value),
+ "expected value and other_value to be of same type"
+) AS value FROM my_table;
+```
+
+```yql
+SELECT EnsureConvertibleTo(
+ value,
+ Double?,
+ "expected value to be numeric"
+) AS value FROM my_table;
+```
+
+## EvaluateExpr, EvaluateAtom {#evaluate_expr_atom}
+
+Evaluate an expression before the start of the main calculation and input its result to the query as a literal (constant). In many contexts, where only a constant would be expected in standard SQL (for example, in table names, in the number of rows in [LIMIT](../syntax/select/limit_offset.md), and so on), this functionality is implicitly enabled automatically.
+
+EvaluateExpr can be used where the grammar already expects an expression. For example, you can use it to:
+
+* Round the current time to days, weeks, or months and insert it into the query to ensure correct [query caching](../syntax/pragma.md#yt.querycachemode), although usually when [functions are used to get the current time](#current-utc), query caching is completely disabled.
+* Run a heavy calculation with a small result once per query instead of once per job.
+
+EvaluateAtom lets you dynamically create an [atom](../types/special.md), but since atoms are mainly controlled from a lower [s-expressions](/docs/s_expressions/functions) level, it's generally not recommended to use this function directly.
+
+The only argument for both functions is the expression for calculation and substitution.
+
+Restrictions:
+
+* The expression must not trigger MapReduce operations.
+* This functionality is fully locked in YQL over YDB.
+
+### Examples
+
+```yql
+$now = CurrentUtcDate();
+SELECT EvaluateExpr(
+ DateTime::MakeDate(DateTime::StartOfWeek($now)
+ )
+);
+```
+
+
+## Literals of primitive types {#data-type-literals}
+
+For primitive types, you can create literals based on string literals.
+
+### Syntax
+
+`<Primitive type>( <string>[, <additional attributes>] )`
+
+Unlike `CAST("myString" AS MyType)`:
+
+* The check for literal's castability to the desired type occurs at validation.
+* The result is non-optional.
+
+For the data types `Date`, `Datetime`, `Timestamp`, and `Interval`, literals are supported only in the format corresponding to [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601). `Interval` has the following differences from the standard:
+
+* It supports the negative sign for shifts to the past.
+* Microseconds can be expressed as fractional parts of seconds.
+* You can't use units of measurement exceeding one week.
+* The options with the beginning/end of the interval and with repetitions, are not supported.
+
+For the data types `TzDate`, `TzDatetime`, `TzTimestamp`, literals are also set in the format meeting [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), but instead of the optional Z suffix, they specify the [IANA name of the time zone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones), separated by comma (for example, GMT or Europe/Moscow).
+
+{% include [decimal args](../_includes/decimal_args.md) %}
+
+### Examples
+
+```yql
+SELECT
+ Bool("true"),
+ Uint8("0"),
+ Int32("-1"),
+ Uint32("2"),
+ Int64("-3"),
+ Uint64("4"),
+ Float("-5"),
+ Double("6"),
+ Decimal("1.23", 5, 2), -- up to 5 decimal digits, with 2 after the decimal point
+ String("foo"),
+ Utf8("Hello"),
+ Yson("<a=1>[3;%false]"),
+ Json(@@{"a":1,"b":null}@@),
+ Date("2017-11-27"),
+ Datetime("2017-11-27T13:24:00Z"),
+ Timestamp("2017-11-27T13:24:00.123456Z"),
+ Interval("P1DT2H3M4.567890S"),
+ TzDate("2017-11-27,Europe/Moscow"),
+ TzDatetime("2017-11-27T13:24:00,America/Los_Angeles"),
+ TzTimestamp("2017-11-27T13:24:00.123456,GMT"),
+ Uuid("f9d5cc3f-f1dc-4d9c-b97e-766e57ca4ccb");
+```
+
+## ToBytes and FromBytes {#to-from-bytes}
+
+Conversion of [primitive data types](../types/primitive.md) to a string with their binary representation and back. Numbers are represented in the [little endian](https://en.wikipedia.org/wiki/Endianness#Little-endian) format.
+
+### Examples
+
+```yql
+SELECT
+ ToBytes(123), -- "\u0001\u0000\u0000\u0000"
+ FromBytes(
+ "\xd2\x02\x96\x49\x00\x00\x00\x00",
+ Uint64
+ ); -- 1234567890ul
+```
+
+
+
+## ByteAt {#byteat}
+
+Getting the byte value inside a string at an index counted from the beginning of the string. If an invalid index is specified, `NULL` is returned.
+
+Arguments:
+
+1. String: `String` or `Utf8`.
+2. Index: `Uint32`.
+
+### Examples
+
+```yql
+SELECT
+ ByteAt("foo", 0), -- 102
+ ByteAt("foo", 1), -- 111
+ ByteAt("foo", 9); -- NULL
+```
+
+
+
+## ...Bit {#bitops}
+
+`TestBit()`, `ClearBit()`, `SetBit()` and `FlipBit()`: Test, clear, set, or flip a bit in an unsigned number using the specified bit sequence number.
+
+Arguments:
+
+1. An unsigned number that's subject to the operation. TestBit is also implemented for strings.
+2. Number of the bit.
+
+TestBit returns `true/false`. The other functions return a copy of their first argument with the corresponding conversion.
+
+### Examples
+
+```yql
+SELECT
+ TestBit(1u, 0), -- true
+ SetBit(8u, 0); -- 9
+```
+
+
+
+## Abs {#abs}
+
+The absolute value of the number.
+
+### Examples
+
+```yql
+SELECT Abs(-123); -- 123
+```
+
+
+
+## Just {#optional-ops}
+
+`Just()`: Change the value's data type to [optional](../types/optional.md) from the current data type (i.e.,`T` is converted to `T?`).
+
+The reverse operation is [Unwrap](#optional-ops).
+
+### Examples
+
+```yql
+SELECT
+ Just("my_string"); -- String?
+```
+
+## Unwrap {#unwrap}
+
+`Unwrap()`: Converting the [optional](../types/optional.md) value of the data type to the relevant non-optional type, raising a runtime error if the data is `NULL`. This means that `T?` becomes `T`.
+
+If the value isn't [optional](../types/optional.md), then the function returns its first argument unchanged.
+
+Arguments:
+
+1. Value to be converted.
+2. An optional string with a comment for the error text.
+
+Reverse operation is [Just](#optional-ops).
+
+### Examples
+
+```yql
+$value = Just("value");
+
+SELECT Unwrap($value, "Unexpected NULL for $value");
+```
+
+## Nothing {#nothing}
+
+`Nothing()`: Create an empty value for the specified [Optional](../types/optional.md) data type.
+
+### Examples
+
+```yql
+SELECT
+ Nothing(String?); -- an empty (NULL) value with the String? type
+```
+
+[Learn more about ParseType and other functions for data types](types.md).
+
+
+
+## Callable {#callable}
+
+Create a callable value with the specified signature from a lambda function. It's usually used to put callable values into containers.
+
+Arguments:
+
+1. Type.
+2. Lambda function.
+
+### Examples
+
+```yql
+$lambda = ($x) -> {
+ RETURN CAST($x as String)
+};
+
+$callables = AsTuple(
+ Callable(Callable<(Int32)->String>, $lambda),
+ Callable(Callable<(Bool)->String>, $lambda),
+);
+
+SELECT $callables.0(10), $callables.1(true);
+```
+
+
+
+## Pickle, Unpickle {#pickle}
+
+`Pickle()` and `StablePickle()` serialize an arbitrary object into a sequence of bytes, if possible. Typical non-serializable objects are Callable and Resource. The serialization format is not versioned and can be used within a single query. For the Dict type, the StablePickle function pre-sorts the keys, and for Pickle, the order of dictionary elements in the serialized representation isn't defined.
+
+`Unpickle()` is the inverse operation (deserialization), where with the first argument being the data type of the result and the second argument is the string with the result of `Pickle()` or `StablePickle()`.
+
+### Examples
+
+```yql
+SELECT *
+FROM my_table
+WHERE Digest::MurMurHash32(
+ Pickle(TableRow())
+ ) %10 ==0; -- actually, it is better to use TABLESAMPLE
+
+$buf = Pickle(123);
+SELECT Unpickle(Int32, $buf);
+```
+
+
+
+## StaticMap
+
+Transforms a structure or tuple by applying a lambda function to each item.
+
+Arguments:
+
+* Structure or tuple.
+* Lambda for processing items.
+
+Result: a structure or tuple with the same number and naming of items as in the first argument, and with item data types determined by lambda results.
+
+### Examples
+
+```yql
+SELECT *
+FROM (
+ SELECT
+ StaticMap(TableRow(), ($item) -> {
+ return CAST($item AS String);
+ })
+ FROM my_table
+) FLATTEN COLUMNS; -- converting all columns to rows
+```
+
+
+
+## StaticZip
+
+Merges structures or tuples element-by-element. All arguments (one or more) must be either structures with the same set of fields or tuples of the same length.
+The result will be a structure or tuple, respectively.
+Each item of the result is a tuple comprised of items taken from arguments.
+
+### Examples
+
+```yql
+$one = <|k1:1, k2:2.0|>;
+$two = <|k1:3.0, k2:4|>;
+
+-- Adding two structures item-by-item
+SELECT StaticMap(StaticZip($one, $two), ($tuple)->($tuple.0 + $tuple.1)) AS sum;
+```
+
+
+
+## StaticFold, StaticFold1 {#staticfold}
+
+```yql
+StaticFold(obj:Struct/Tuple, initVal, updateLambda)
+StaticFold1(obj:Struct/Tuple, initLambda, updateLambda)
+```
+
+Left fold over struct/tuple elements.
+The folding of tuples is done in order from the element with the lower index to the element with the larger one; for structures, the order is not guaranteed.
+
+- `obj` - object to fold
+- `initVal` - _(for StaticFold)_ initial fold state
+- `initLambda` - _(for StaticFold1)_ lambda that produces initial fold state from the first element
+- `updateLambda` - lambda that produces the new state (arguments are the next element and the previous state)
+
+
+`StaticFold(<|key_1:$el_1, key_2:$el_2, ..., key_n:$el_n|>, $init, $f)` transforms into:
+
+```yql
+$f($el_n, ...$f($el_2, $f($init, el_1))...)
+```
+
+`StaticFold1(<|key_1:$el_1, key_2:$el_2, ..., key_n:$el_n|>, $f0, $f)`:
+
+```yql
+$f($el_n, ...$f($el_2, $f($f0($init), el_1))...)
+```
+
+`StaticFold1(<||>, $f0, $f)` returns `NULL`.
+
+Works with tuples in the same way.
+
+
+## AggregationFactory {#aggregationfactory}
+
+Create a factory for [aggregation functions](aggregation.md) to separately describe the methods of aggregation and data types subject to aggregation.
+
+Arguments:
+
+1. A string in double quotes with the name of an aggregate function, for example ["MIN"](aggregation.md#min).
+2. Optional parameters of the aggregate function that are data-independent. For example, the percentile value in [PERCENTILE](aggregation.md#percentile).
+
+The resulting factory can be used as the second parameter of the function [AGGREGATE_BY](aggregation.md#aggregate-by).
+If the aggregate function is applied to two columns instead of one, as, for example, [MIN_BY](aggregation.md#minby), then in [AGGREGATE_BY](aggregation.md#aggregate-by), the first argument passes a `Tuple` of two values. See more details in the description of the applicable aggregate function.
+
+### Examples
+
+```yql
+$factory = AggregationFactory("MIN");
+SELECT
+ AGGREGATE_BY (value, $factory) AS min_value -- apply the MIN aggregation to the "value" column
+FROM my_table;
+```
+
+## AggregateTransformInput {#aggregatetransform}
+
+`AggregateTransformInput()` converts an [aggregation factory](aggregation.md), for example, obtained using the [AggregationFactory](#aggregationfactory) function, to other factory, in which the specified transformation of input items is performed before starting aggregation.
+
+Arguments:
+
+1. Aggregation factory.
+2. A lambda function with one argument that converts an input item.
+
+### Examples
+
+```yql
+$f = AggregationFactory("sum");
+$g = AggregateTransformInput($f, ($x) -> (cast($x as Int32)));
+$h = AggregateTransformInput($f, ($x) -> ($x * 2));
+SELECT ListAggregate([1,2,3], $f); -- 6
+SELECT ListAggregate(["1","2","3"], $g); -- 6
+SELECT ListAggregate([1,2,3], $h); -- 12
+```
+
+## AggregateTransformOutput {#aggregatetransformoutput}
+
+`AggregateTransformOutput()` converts an [aggregation factory](aggregation.md), for example, obtained using the [AggregationFactory](#aggregationfactory) function, to other factory, in which the specified transformation of the result is performed after ending aggregation.
+
+Arguments:
+
+1. Aggregation factory.
+2. A lambda function with one argument that converts the result.
+
+### Examples
+
+```yql
+$f = AggregationFactory("sum");
+$g = AggregateTransformOutput($f, ($x) -> ($x * 2));
+SELECT ListAggregate([1,2,3], $f); -- 6
+SELECT ListAggregate([1,2,3], $g); -- 12
+```
+
+## AggregateFlatten {#aggregateflatten}
+
+Adapts a factory for [aggregation functions](aggregation.md), for example, obtained using the [AggregationFactory](#aggregationfactory) function in a way that allows aggregation of list input items. This operation is similar to [FLATTEN LIST BY](../syntax/flatten.md): Each list item is aggregated.
+
+Arguments:
+
+1. Aggregation factory.
+
+### Examples
+
+```yql
+$i = AggregationFactory("AGGREGATE_LIST_DISTINCT");
+$j = AggregateFlatten($i);
+SELECT AggregateBy(x, $j) from (
+ select [1,2] as x
+ union all
+ select [2,3] as x
+); -- [1, 2, 3]
+```
+
+## YQL::, s-expressions {#s-expressions}
+
+For the full list of internal YQL functions, see the [documentation for s-expressions](/docs/s_expressions/functions), an alternative low-level YQL syntax. Any of the functions listed there can also be called from the SQL syntax by adding the `YQL::` prefix to its name. However, we don't recommend doing this, because this mechanism is primarily intended to temporarily bypass possible issues and for internal testing purposes.
+
+If the function is available in SQL syntax without the `YQL::` prefix, then its behavior may differ from the same-name function from the s-expressions documentation, if any.
+
diff --git a/yql/essentials/docs/en/builtins/codegen.md b/yql/essentials/docs/en/builtins/codegen.md
new file mode 100644
index 00000000000..2728742947a
--- /dev/null
+++ b/yql/essentials/docs/en/builtins/codegen.md
@@ -0,0 +1,181 @@
+# Functions for code generation
+
+When running calculations, you can generate the code including [S-expressions](/docs/s_expressions) nodes. This uses a mechanism for packing the code in the [resource](../types/special.md). After building the code, you can insert it into the main program using the [EvaluateCode](#evaluatecode) function. For debugging purposes, you can convert the code to a string using the [FormatCode](#formatcode) function.
+
+Possible node types in S-expressions that can be used for code generation:
+
+* An atom is an untyped string of zero or more characters.
+* A list is a sequence of zero or more nodes. It corresponds to the `tuple` type in SQL.
+* A call of a built-in function consists of a name expressed by an atom and a sequence of zero or more nodes that are arguments to this function.
+* Lambda function declaration consists of declaring the names of arguments and a node that is the root of the body for this lambda function.
+* The lambda function argument is a node that can only be used inside the body of the lambda function.
+* World is a special node that labels I/O operations.
+
+The S-expressions nodes form a directed graph. Atoms are always leaf nodes, because they cannot contain child nodes.
+
+In the text representation, S-expressions have the following format:
+
+* Atom: `'"foo"`. The apostrophe character (') denotes quoting of the next line that is usually enclosed in quotation marks.
+* List: `'("foo" "bar")`. The apostrophe character (') denotes that there will be no function call in parentheses.
+* Calling the built-in function: `(foo "bar")`. The first item inside the brackets is the mandatory name of the function followed by the function arguments.
+* Declaring a lambda function: `(lambda '(x y) (+ x y))`. The `lambda` keyword is followed by a list of argument names and then by the body of the lambda function.
+* The lambda function argument is `x`. Unlike an atom, a string without an apostrophe character (') references a name in the current scope. When declaring a lambda function, the names of arguments are added to the body's visibility scope, and, if needed, the name is hidden from the global scope.
+* The `world`.
+
+## FormatCode
+
+Serializing the code as [S-expressions](/docs/s_expressions). The code must not contain free arguments of functions, hence, to serialize the lambda function code, you must pass it completely, avoiding passing individual expressions that might contain lambda function arguments.
+
+### Examples
+
+```yql
+SELECT FormatCode(AtomCode("foo"));
+-- (
+-- (return '"foo")
+-- )
+```
+
+## WorldCode
+
+Build a code node with the `world` type.
+
+### Examples
+
+```yql
+SELECT FormatCode(WorldCode());
+-- (
+-- (return world)
+-- )
+```
+
+## AtomCode
+
+Build a code node with the `atom` type from a string passed to the argument.
+
+### Examples
+
+```yql
+SELECT FormatCode(AtomCode("foo"));
+-- (
+-- (return '"foo")
+-- )
+```
+
+## ListCode
+
+Build a code node with the `list` type from a set of nodes or lists of code nodes passed to arguments. In this case, lists of arguments are built in as separately listed code nodes.
+
+### Examples
+
+```yql
+SELECT FormatCode(ListCode(
+ AtomCode("foo"),
+ AtomCode("bar")));
+-- (
+-- (return '('"foo" '"bar"))
+-- );
+
+SELECT FormatCode(ListCode(AsList(
+ AtomCode("foo"),
+ AtomCode("bar"))));
+-- (
+-- (return '('"foo" '"bar"))
+-- )
+```
+
+## FuncCode
+
+Build a code node with the `built-in function call` from a string with the function name and a set of nodes or lists of code nodes passed to arguments. In this case, lists of arguments are built in as separately listed code nodes.
+
+### Examples
+
+```yql
+SELECT FormatCode(FuncCode(
+ "Baz",
+ AtomCode("foo"),
+ AtomCode("bar")));
+-- (
+-- (return (Baz '"foo" '"bar"))
+-- )
+
+SELECT FormatCode(FuncCode(
+ "Baz",
+ AsList(
+ AtomCode("foo"),
+ AtomCode("bar"))));
+-- (
+-- (return (Baz '"foo" '"bar"))
+-- )
+```
+
+## LambdaCode
+
+You can build a code node with the `lambda function declaration` type from:
+
+* [a Lambda function](../syntax/expressions.md#lambda), if you know the number of arguments in advance. In this case, the nodes of the `argument` type will be passed as arguments to this lambda function.
+* The number of arguments and a [lambda function](../syntax/expressions.md#lambda) with one argument. In this case, a list of nodes of the `argument`type will be passed as an argument to this lambda function.
+
+### Examples
+
+```yql
+SELECT FormatCode(LambdaCode(($x, $y) -> {
+ RETURN FuncCode("+", $x, $y);
+}));
+-- (
+-- (return (lambda '($1 $2) (+ $1 $2)))
+-- )
+
+SELECT FormatCode(LambdaCode(2, ($args) -> {
+ RETURN FuncCode("*", Unwrap($args[0]), Unwrap($args[1]));
+}));
+-- (
+-- (return (lambda '($1 $2) (* $1 $2)))
+-- )
+```
+
+## EvaluateCode
+
+Substituting the code node passed in the argument, into the main program code.
+
+### Examples
+
+```yql
+SELECT EvaluateCode(FuncCode("Int32", AtomCode("1"))); -- 1
+
+$lambda = EvaluateCode(LambdaCode(($x, $y) -> {
+ RETURN FuncCode("+", $x, $y);
+}));
+SELECT $lambda(1, 2); -- 3
+```
+
+## ReprCode
+
+Substituting the code node representing the result of evaluating an expression passed in the argument, into the main program.
+
+### Examples
+
+```yql
+$add3 = EvaluateCode(LambdaCode(($x) -> {
+ RETURN FuncCode("+", $x, ReprCode(1 + 2));
+}));
+SELECT $add3(1); -- 4
+```
+
+## QuoteCode
+
+Substituting into the main program the code node that represents an expression or a [lambda function](../syntax/expressions.md#lambda) passed in the argument. If free arguments of lambda functions were found during the substitution, they are calculated and substituted into the code as in the [ReprCode](#reprcode) function.
+
+### Examples
+
+```yql
+$lambda = ($x, $y) -> { RETURN $x + $y };
+$makeClosure = ($y) -> {
+ RETURN EvaluateCode(LambdaCode(($x) -> {
+ RETURN FuncCode("Apply", QuoteCode($lambda), $x, ReprCode($y))
+ }))
+};
+
+$closure = $makeClosure(2);
+SELECT $closure(1); -- 3
+```
+
diff --git a/yql/essentials/docs/en/builtins/dict.md b/yql/essentials/docs/en/builtins/dict.md
new file mode 100644
index 00000000000..5659d9ff50e
--- /dev/null
+++ b/yql/essentials/docs/en/builtins/dict.md
@@ -0,0 +1,282 @@
+# Functions for dictionaries
+
+## DictCreate {#dictcreate}
+
+Construct an empty dictionary. Two arguments are passed: for a key and a value. Each argument specifies a string with the data type declaration or the type itself built by [type functions](types.md). There are no dictionaries with an unknown key or value type in YQL. As a key, you can set a [primitive data type](../types/primitive.md), except for `Yson` and `Json` that may be [optional](../types/optional.md) or a tuple of them of a length of at least two.
+
+[Documentation for the type definition format](../types/type_string.md).
+
+### Examples
+
+```yql
+SELECT DictCreate(String, Tuple<String,Double?>);
+```
+
+```yql
+SELECT DictCreate(Tuple<Int32?,String>, OptionalType(DataType("String")));
+```
+
+## SetCreate {#setcreate}
+
+Construct an empty set. An argument is passed: the key type that can be built by [type functions](types.md). There are no sets with an unknown key type in YQL. As a key, you can set a [primitive data type](../types/primitive.md), except for `Yson` and `Json` that may be [optional](../types/optional.md) or a tuple of them of a length of at least two.
+
+[Documentation for the type definition format](../types/type_string.md).
+
+### Examples
+
+```yql
+SELECT SetCreate(String);
+```
+
+```yql
+SELECT SetCreate(Tuple<Int32?,String>);
+```
+
+## DictLength {#dictlength}
+
+The count of items in the dictionary.
+
+### Examples
+
+```yql
+SELECT DictLength(AsDict(AsTuple(1, AsList("foo", "bar"))));
+```
+
+```yql
+SELECT DictLength(dict_column) FROM my_table;
+```
+
+## DictHasItems {#dicthasitems}
+
+Check that the dictionary contains at least one item.
+
+### Examples
+
+```yql
+SELECT DictHasItems(AsDict(AsTuple(1, AsList("foo", "bar")))) FROM my_table;
+```
+
+```yql
+SELECT DictHasItems(dict_column) FROM my_table;
+```
+
+
+## DictItems {#dictitems}
+
+Get dictionary contents as a list of tuples including key-value pairs (`List<Tuplekey_type,value_type>`).
+
+### Examples
+
+```yql
+SELECT DictItems(AsDict(AsTuple(1, AsList("foo", "bar"))));
+-- [ ( 1, [ "foo", "bar" ] ) ]
+```
+
+
+```yql
+SELECT DictItems(dict_column)
+FROM my_table;
+```
+
+
+## DictKeys {#dictkeys}
+
+Get a list of dictionary keys.
+
+### Examples
+
+```yql
+SELECT DictKeys(AsDict(AsTuple(1, AsList("foo", "bar"))));
+-- [ 1 ]
+```
+
+
+```yql
+SELECT DictKeys(dict_column)
+FROM my_table;
+```
+
+
+## DictPayloads {#dictpayloads}
+
+Get a list of dictionary values.
+
+### Examples
+
+```yql
+SELECT DictPayloads(AsDict(AsTuple(1, AsList("foo", "bar"))));
+-- [ [ "foo", "bar" ] ]
+```
+
+```yql
+SELECT DictPayloads(dict_column)
+FROM my_table;
+```
+
+## DictLookup {#dictlookup}
+
+Get a dictionary element by its key.
+
+### Examples
+
+```yql
+SELECT DictLookup(AsDict(
+ AsTuple(1, AsList("foo", "bar")),
+ AsTuple(2, AsList("bar", "baz"))
+), 1);
+-- [ "foo", "bar" ]
+```
+
+```yql
+SELECT DictLookup(dict_column, "foo")
+FROM my_table;
+```
+
+## DictContains {#dictcontains}
+
+Checking if an element in the dictionary using its key. Returns true or false.
+
+### Examples
+
+```yql
+SELECT DictContains(AsDict(
+ AsTuple(1, AsList("foo", "bar")),
+ AsTuple(2, AsList("bar", "baz"))
+), 42);
+-- false
+```
+
+```yql
+SELECT DictContains(dict_column, "foo")
+FROM my_table;
+```
+
+
+## DictAggregate {#dictaggregate}
+
+Apply [aggregation factory](basic.md#aggregationfactory) to the passed dictionary where each value is a list. The factory is applied separately inside each key.
+If the list is empty, the aggregation result is the same as for an empty table: 0 for the `COUNT` function and `NULL` for other functions.
+If the list under a certain key is empty in the passed dictionary, such a key is removed from the result.
+If the passed dictionary is optional and contains `NULL`, the result is also `NULL`.
+
+Arguments:
+
+1. Dictionary.
+2. [Aggregation factory](basic.md#aggregationfactory).
+
+### Examples
+
+```yql
+SELECT DictAggregate(AsDict(
+ AsTuple(1, AsList("foo", "bar")),
+ AsTuple(2, AsList("baz", "qwe"))),
+ AggregationFactory("Max"));
+-- {1 : "foo", 2 : "qwe" }
+```
+
+## SetIsDisjoint {#setisjoint}
+
+Check that the dictionary doesn't intersect by keys with a list or another dictionary.
+
+So there are two options to make a call:
+
+* With the `Dict<K,V1>` and `List<K>` arguments.
+* With the `Dict<K,V1>` and `Dict<K,V2>` arguments.
+
+### Examples
+
+```yql
+SELECT SetIsDisjoint(ToSet(AsList(1, 2, 3)), AsList(7, 4)); -- true
+SELECT SetIsDisjoint(ToSet(AsList(1, 2, 3)), ToSet(AsList(3, 4))); -- false
+```
+
+## SetIntersection {#setintersection}
+
+Construct intersection between two dictionaries based on keys.
+
+Arguments:
+
+* Two dictionaries: `Dict<K,V1>` and `Dict<K,V2>`.
+* An optional function that combines the values from the source dictionaries to construct the values of the output dictionary. If such a function has the `(K,V1,V2) -> U` type, the result type is `Dict<K,U>`. If the function is not specified, the result type is `Dict<K,Void>`, and the values from the source dictionaries are ignored.
+
+### Examples
+
+```yql
+SELECT SetIntersection(ToSet(AsList(1, 2, 3)), ToSet(AsList(3, 4))); -- { 3 }
+SELECT SetIntersection(
+ AsDict(AsTuple(1, "foo"), AsTuple(3, "bar")),
+ AsDict(AsTuple(1, "baz"), AsTuple(2, "qwe")),
+ ($k, $a, $b) -> { RETURN AsTuple($a, $b) });
+-- { 1 : ("foo", "baz") }
+```
+
+## SetIncludes {#setincludes}
+
+Checking that the keys of the specified dictionary include all the elements of the list or the keys of the second dictionary.
+
+So there are two options to make a call:
+
+* With the `Dict<K,V1>` and `List<K>` arguments.
+* With the `Dict<K,V1>` and `Dict<K,V2>` arguments.
+
+### Examples
+
+```yql
+SELECT SetIncludes(ToSet(AsList(1, 2, 3)), AsList(3, 4)); -- false
+SELECT SetIncludes(ToSet(AsList(1, 2, 3)), ToSet(AsList(2, 3))); -- true
+```
+
+## SetUnion {#setunion}
+
+Constructs a union of two dictionaries based on keys.
+
+Arguments:
+
+* Two dictionaries: `Dict<K,V1>` and `Dict<K,V2>`.
+* An optional function that combines the values from the source dictionaries to construct the values of the output dictionary. If such a function has the `(K,V1?,V2?) -> U` type, the result type is `Dict<K,U>`. If the function is not specified, the result type is `Dict<K,Void>`, and the values from the source dictionaries are ignored.
+
+### Examples
+
+```yql
+SELECT SetUnion(ToSet(AsList(1, 2, 3)), ToSet(AsList(3, 4))); -- { 1, 2, 3, 4 }
+SELECT SetUnion(
+ AsDict(AsTuple(1, "foo"), AsTuple(3, "bar")),
+ AsDict(AsTuple(1, "baz"), AsTuple(2, "qwe")),
+ ($k, $a, $b) -> { RETURN AsTuple($a, $b) });
+-- { 1 : ("foo", "baz"), 2 : (null, "qwe"), 3 : ("bar", null) }
+```
+
+## SetDifference {#setdifference}
+
+Construct a dictionary containing all the keys with their values in the first dictionary with no matching key in the second dictionary.
+
+### Examples
+
+```yql
+SELECT SetDifference(ToSet(AsList(1, 2, 3)), ToSet(AsList(3, 4))); -- { 1, 2 }
+SELECT SetDifference(
+ AsDict(AsTuple(1, "foo"), AsTuple(2, "bar")),
+ ToSet(AsList(2, 3)));
+-- { 1 : "foo" }
+```
+
+## SetSymmetricDifference {#setsymmetricdifference}
+
+Construct a symmetric difference between two dictionaries based on keys.
+
+Arguments:
+
+* Two dictionaries: `Dict<K,V1>` and `Dict<K,V2>`.
+* An optional function that combines the values from the source dictionaries to construct the values of the output dictionary. If such a function has the `(K,V1?,V2?) -> U` type, the result type is `Dict<K,U>`. If the function is not specified, the result type is `Dict<K,Void>`, and the values from the source dictionaries are ignored.
+
+### Examples
+
+```yql
+SELECT SetSymmetricDifference(ToSet(AsList(1, 2, 3)), ToSet(AsList(3, 4))); -- { 1, 2, 4 }
+SELECT SetSymmetricDifference(
+ AsDict(AsTuple(1, "foo"), AsTuple(3, "bar")),
+ AsDict(AsTuple(1, "baz"), AsTuple(2, "qwe")),
+ ($k, $a, $b) -> { RETURN AsTuple($a, $b) });
+-- { 2 : (null, "qwe"), 3 : ("bar", null) }
+```
+
diff --git a/yql/essentials/docs/en/builtins/index.md b/yql/essentials/docs/en/builtins/index.md
new file mode 100644
index 00000000000..5c303e16d07
--- /dev/null
+++ b/yql/essentials/docs/en/builtins/index.md
@@ -0,0 +1,12 @@
+# Built-in YQL functions
+
+- [Basic](basic.md)
+- [Aggregate](aggregation.md)
+- [Window](window.md)
+- [For lists](list.md)
+- [For dictionaries](dict.md)
+- [For structures](struct.md)
+- [For types](types.md)
+- [For code generation](codegen.md)
+- [For JSON](json.md)
+- [C++ libraries](../udf/list/index.md)
diff --git a/yql/essentials/docs/en/builtins/json.md b/yql/essentials/docs/en/builtins/json.md
new file mode 100644
index 00000000000..d9168fa6d1d
--- /dev/null
+++ b/yql/essentials/docs/en/builtins/json.md
@@ -0,0 +1,1170 @@
+# Functions for JSON
+
+<!-- markdownlint-disable no-trailing-punctuation -->
+
+**JSON** is a lightweight [data-interchange format](https://www.json.org). In YQL, it's represented by the `Json` type. Unlike relational tables, JSON can store data with no schema defined. Here is an example of a valid JSON object:
+
+```json
+[
+ {
+ "name": "Jim Holden",
+ "age": 30
+ },
+ {
+ "name": "Naomi Nagata",
+ "age": "twenty years old"
+ }
+]
+```
+
+Despite the fact that the `age` field in the first object is of the `Number` type (`"age": 21`) and in the second object its type is `String` (`"age": "twenty years old"`), this is a fully valid JSON object.
+
+To work with JSON, YQL implements a subset of the [SQL support for JavaScript Object Notation (JSON)](https://www.iso.org/standard/67367.html) standard, which is part of the common ANSI SQL standard.
+
+## JsonPath
+
+Values inside JSON objects are accessed using a query language called JsonPath. All functions for JSON accept a JsonPath query as an argument.
+
+Let's look at an example. Suppose we have a JSON object like:
+
+```json
+{
+ "comments": [
+ {
+ "id": 123,
+ "text": "A whisper will do, if it's all that you can manage."
+ },
+ {
+ "id": 456,
+ "text": "My life has become a single, ongoing revelation that I haven’t been cynical enough."
+ }
+ ]
+}
+```
+
+Then, to get the text of the second comment, we can write the following JsonPath query:
+
+```yql
+$.comments[1].text
+```
+
+In this query:
+
+1. `$` is a way to access the entire JSON object.
+2. `$.comments` accesses the `comments` key of the JSON object.
+3. `$.comments[1]` accesses the second element of the JSON array (element numbering starts from 0).
+4. `$.comments[1].text` accesses the `text` key of the JSON object.
+5. Query execution result: `"My life has become a single, ongoing revelation that I haven’t been cynical enough."`
+
+### Quick reference
+
+| Operation | Example |
+| --------------------------------------- | ------------------------------------------------- |
+| Retrieving a JSON object key | `$.key` |
+| Retrieving all JSON object keys | `$.*` |
+| Accessing an array element | `$[25]` |
+| Retrieving an array subsegment | `$[2 to 5]` |
+| Accessing the last array element | `$[last]` |
+| Accessing all array elements | `$[*]` |
+| Unary operations | `- 1` |
+| Binary operations | `(12 * 3) % 4 + 8` |
+| Accessing a variable | `$variable` |
+| Logical operations | `(1 > 2) &#124;&#124; (3 <= 4) && ("string" == "another")&#124; |
+| Matching a regular expression | `$.name like_regex "^[A-Za-z]+$"` |
+| Checking the string prefix | `$.name starts with "Bobbie"` |
+| Checking if a path exists | `exists ($.profile.name)` |
+| Checking a Boolean expression for null | `($.age > 20) is unknown` |
+| Filtering values | `$.friends ? (@.age >= 18 && @.gender == "male")` |
+| Getting the value type | `$.name.type()` |
+| Getting the array size | `$.friends.size()` |
+| Converting a string to a number | `$.number.double()` |
+| Rounding up a number | `$.number.ceiling()` |
+| Rounding down a number | `$.number.floor()` |
+| Returning the absolute value | `$.number.abs()` |
+| Getting key-value pairs from an object | `$.profile.keyvalue()` |
+
+### Data model
+
+The result of executing all JsonPath expressions is a sequence of JSON values. For example:
+
+- The result of executing the `"Bobbie"` expression is a sequence with the only element `"Bobbie"`. Its length is 1.
+- The result of executing the `$` expression (that takes the entire JSON object) in JSON `[1, 2, 3]` is `[1, 2, 3]`. A sequence of 1 element of the array `[1, 2, 3]`
+- The result of executing the `$[*]` expression (retrieving all array elements) in JSON `[1, 2, 3]` is `1, 2, 3`. A sequence of three items:`1`, `2`, and `3`
+
+If the input sequence consists of multiple values, some operations are performed for each element (for example, accessing a JSON object key). However, other operations require a sequence of one element as input (for example, binary arithmetic operations).
+
+The behavior of a specific operation is described in the corresponding section of the documentation.
+
+### Execution mode
+
+JsonPath supports two execution modes, `lax` and `strict`. Setting the mode is optional. By default, `lax`. The mode is specified at the beginning of a query. For example, `strict $.key`.
+
+The behavior for each mode is described in the corresponding sections with JsonPath operations.
+
+#### Auto unpacking of arrays
+
+When accessing a JSON object key in `lax` mode, arrays are automatically unpacked.
+
+##### Example
+
+```json
+[
+ {
+ "key": 123
+ },
+ {
+ "key": 456
+ }
+]
+```
+
+The `lax $.key` query is successful and returns `123, 456`. As `$` is an array, it's automatically unpacked and accessing the key of the `$.key` JSON object is executed for each element in the array.
+
+The `strict $.key` query returns an error. In `strict` mode, there is no support for auto unpacking of arrays. Since `$` is an array and not an object, accessing the `$.key` object key is impossible. You can fix this by writing `strict $[*].key`.
+
+Unpacking is only 1 level deep. In the event of nested arrays, only the outermost one is unpacked.
+
+#### Wrapping values in arrays
+
+When accessing an array element in `lax` mode, JSON values are automatically wrapped in an array.
+
+##### Example
+
+```json
+{
+ "name": "Avasarala"
+}
+```
+
+The `lax $[0].name` query is successful and returns `"Avasarala"`. As `$` isn't an array, it's automatically wrapped in an array of length 1. Accessing the first element `$[0]` returns the source JSON object where the `name` key is taken.
+
+The `strict $[0].name` query returns an error. In `strict` mode, values aren't wrapped in an array automatically. Since `$` is an object and not an array, accessing the `$[0]` element is impossible. You can fix this by writing `strict $.name`.
+
+#### Handling errors
+
+Some errors are converted to an empty result when a query is executed in `lax` mode.
+
+### Literals
+
+Values of some types can be specified in a JsonPath query using literals:
+
+| Type | Example |
+| ------------------ | ---------------- |
+| Numbers | `42`, `-1.23e-5` |
+| Boolean values | `false`, `true` |
+| Null | `Null` |
+| Stings | `"Belt"` |
+
+### Accessing JSON object keys
+
+JsonPath supports accessing JSON object keys, such as `$.session.user.name`.
+
+{% note info %}
+
+Accessing keys without quotes is only supported for keys that start with an English letter or underscore and only contain English letters, underscores, numbers, and a dollar sign. Use quotes for all other keys. For example, `$.profile."this string has spaces"` or `$.user."42 is the answer"`
+
+{% endnote %}
+
+For each value from the input sequence:
+
+1. If the value is an array, it's automatically unpacked in `lax` mode.
+2. If the value isn't a JSON object or if it is and the specified key is missing from this JSON object, a query executed in `strict` mode fails. In `lax` mode, an empty result is returned for this value.
+
+The expression execution result is the concatenation of the results for each value from the input sequence.
+
+#### Example
+
+```json
+{
+ "name": "Amos",
+ "friends": [
+ {
+ "name": "Jim"
+ },
+ {
+ "name": "Alex"
+ }
+ ]
+}
+```
+
+| | `lax` | `strict` |
+| ---------------- | ---------------- | -------- |
+| `$.name` | `"Amos"` | `"Amos"` |
+| `$.surname` | Empty result | Error |
+| `$.friends.name` | `"Jim", "Alex"` | Error |
+
+### Accessing all JSON object keys
+
+JsonPath supports accessing all JSON object keys at once: `$.*`.
+
+For each value from the input sequence:
+
+1. If the value is an array, it's automatically unpacked in `lax` mode.
+2. If the value isn't a JSON object, a query executed in `strict` mode fails. In `lax` mode, an empty result is returned for this value.
+
+The expression execution result is the concatenation of the results for each value from the input sequence.
+
+#### Example
+
+```json
+{
+ "profile": {
+ "id": 123,
+ "name": "Amos"
+ },
+ "friends": [
+ {
+ "name": "Jim"
+ },
+ {
+ "name": "Alex"
+ }
+ ]
+}
+```
+
+| | `lax` | `strict` |
+| ------------- | --------------- | ------------- |
+| `$.profile.*` | `123, "Amos"` | `123, "Amos"` |
+| `$.friends.*` | `"Jim", "Alex"` | Error |
+
+### Accessing an array element
+
+JsonPath supports accessing array elements: `$.friends[1, 3 to last - 1]`.
+
+For each value from the input sequence:
+
+1. If the value isn't an array, a query executed in `strict` mode fails. In `lax` mode, values are automatically wrapped in an array.
+2. The `last` keyword is replaced with the array's last index. Using `last` outside of accessing the array is an error in both modes.
+3. The specified indexes are calculated. Each of them must be a single number, otherwise the query fails in both modes.
+4. If the index is a fractional number, it's rounded down.
+5. If the index goes beyond the array boundaries, the query executed in `strict` mode fails. In `lax` mode, this index is ignored.
+6. If a segment is specified and its start index is greater than the end index (for example, `$[20 to 1]`), the query fails in `strict` mode. In `lax` mode, this segment is ignored.
+7. All elements by the specified indexes are added to the result. Segments include **both ends**.
+
+#### Examples
+
+```json
+[
+ {
+ "name": "Camina",
+ "surname": "Drummer"
+ },
+ {
+ "name": "Josephus",
+ "surname": "Miller"
+ },
+ {
+ "name": "Bobbie",
+ "surname": "Draper"
+ },
+ {
+ "name": "Julie",
+ "surname": "Mao"
+ }
+]
+```
+
+| | `lax` | `strict` |
+| ----------------------------- | ------------------------------- | ------------------------------- |
+| `$[0].name` | `"Camina"` | `"Camina"` |
+| `$[1, 2 to 3].name` | `"Josephus", "Bobbie", "Julie"` | `"Josephus", "Bobbie", "Julie"` |
+| `$[last - 2].name` | `"Josephus"` | `"Josephus"` |
+| `$[2, last + 200 to 50].name` | `"Bobbie"` | Error |
+| `$[50].name` | Empty result | Error |
+
+### Accessing all array elements
+
+JsonPath supports accessing all array elements at once: `$[*]`.
+
+For each value from the input sequence:
+
+1. If the value isn't an array, a query executed in `strict` mode fails. In `lax` mode, values are automatically wrapped in an array.
+2. All elements of the current array are added to the result.
+
+#### Examples
+
+```json
+[
+ {
+ "class": "Station",
+ "title": "Medina"
+ },
+ {
+ "class": "Corvette",
+ "title": "Rocinante"
+ }
+]
+```
+
+| | `lax` | `strict` |
+| ------------------- | ------------------------- | ----------------------- |
+| `$[*].title` | `"Medina", "Rocinante"` | `"Medina", "Rocinante"` |
+| `lax $[0][*].class` | `"Station"` | Error |
+
+Let's analyze the last example step by step:
+
+1. `$[0]` returns the first element of the array, that is `{"class": "Station", "title": "Medina"}`
+2. `$[0][*]` expects an array for input, but an object was input instead. It's automatically wrapped in an array as `[ {"class": "Station", "title": "Medina"} ]`
+3. Now, `$[0][*]` can be executed and returns all elements of the array, that is `{"class": "Station", "title": "Medina"}`
+4. `$[0][*].class` returns the `class` field value: `"Station"`.
+
+### Arithmetic operations
+
+{% note info %}
+
+All arithmetic operations work with numbers as with Double. Calculations are made with potential [loss of accuracy](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html).
+
+{% endnote %}
+
+#### Unary operations
+
+JsonPath supports unary `+` and `-`.
+
+A unary operation applies to all values from the input sequence. If a unary operation's input is a value that isn't a number, a query fails in both modes.
+
+##### Example
+
+```json
+[1, 2, 3, 4]
+```
+
+The `strict -$[*]` query is successful and returns `-1, -2, -3, -4`.
+
+The `lax -$` query fails as `$` is an array and not a number.
+
+#### Binary operations
+
+JsonPath supports binary arithmetic operations (in descending order of priority):
+
+1. Multiplication `*`, dividing floating-point numbers `/`, and taking the remainder `%` (works as the `MOD` function in `SQL`).
+2. Addition `+`, subtraction `-`.
+
+You can change the order of operations using parentheses.
+
+If each argument of a binary operation is not a single number or a number is divided by 0, the query fails in both modes.
+
+##### Examples
+
+- `(1 + 2) * 3` returns `9`
+- `1 / 2` returns `0.5`
+- `5 % 2` returns `1`
+- `1 / 0` fails
+- If JSON is `[-32.4, 5.2]`, the `$[0] % $[1]` query returns `-1.2`
+- If JSON is `[1, 2, 3, 4]`, the `lax $[*] + $[*]` query fails as the `$[*]` expression execution result is `1, 2, 3, 4`, that is multiple numbers. A binary operation only requires one number for each of its arguments.
+
+### Boolean values
+
+Unlike some other programming languages, Boolean values in JsonPath are not only `true` and `false`, but also `null` (uncertainty).
+
+JsonPath considers any values received from a JSON document to be non-Boolean. For example, a query like `! $.is_valid_user` (a logical negation applied to the `is_valid_user`) field is syntactically invalid because the `is_valid_user` field value is not Boolean (even when it actually stores `true` or `false`). The correct way to write this kind of query is to explicitly use a comparison with a Boolean value, such as `$.is_valid_user == false`.
+
+### Logical operations
+
+JsonPath supports some logical operations for Boolean values.
+
+The arguments of any logical operation must be a single Boolean value.
+All logical operations return a Boolean value.
+
+#### Logical negation, !
+
+Truth table:
+
+| `x` | `!x` |
+| ------- | ------- |
+| `true` | `false` |
+| `false` | `true` |
+| `Null` | `Null` |
+
+#### Logical AND, &&
+
+In the truth table, the first column is the left argument, the first row is the right argument, and each cell is the result of using the Logical AND both with the left and right arguments:
+
+| `&&` | `true` | `false` | `Null` |
+| ------- | ------- | ------- | ------- |
+| `true` | `true` | `false` | `Null` |
+| `false` | `false` | `false` | `false` |
+| `Null` | `Null` | `false` | `Null` |
+
+#### Logical OR, ||
+
+In the truth table, the first column is the left argument, the first row is the right argument, and each cell is the result of using the logical OR with both the left and right arguments:
+
+| `\|\|` | `true` | `false` | `Null` |
+| ------- | ------- | ------- | ------- |
+| `true` | `true` | `true` | `true` |
+| `false` | `true` | `false` | `Null` |
+| `Null` | `true` | `Null` | `Null` |
+
+##### Examples
+
+- `! (true == true)`, the result is `false`
+- `(true == true) && (true == false)`, the result is `false`
+- `(true == true) || (true == false)`, the result is `true`
+
+### Comparison operators
+
+JsonPath implements comparison operators for values:
+
+- Equality, `==`
+- Inequality, `!=`and `<>`
+- Less than and less than or equal to, `<` and `=`
+- Greater than and greater than or equal to, `>` and `>=`
+
+All comparison operators return a Boolean value. Both operator arguments support multiple values.
+
+If an error occurs when calculating the operator arguments, it returns `null`. In this case, the JsonPath query execution continues.
+
+The arrays of each of the arguments are automatically unpacked. After that, for each pair where the first element is taken from the sequence of the left argument and the second one from the sequence of the right argument:
+
+1. The elements of the pair are compared
+2. If an error occurs during the comparison, the `ERROR` flag is set.
+3. If the comparison result is true, the flag set is `FOUND`
+4. If either the `ERROR` or `FOUND` flag is set and the query is executed in `lax` mode, no more pairs are analyzed.
+
+If the pair analysis results in:
+
+1. The `ERROR` flag is set, the operator returns `null`
+2. The `FOUND` flag is set, the operator returns `true`
+3. Otherwise, it returns `false`
+
+We can say that this algorithm considers all pairs from the Cartesian product of the left and right arguments, trying to find the pair whose comparison returns true.
+
+Elements in a pair are compared according to the following rules:
+
+1. If the left or right argument is an array or object, the comparison fails.
+2. `null == null` returns true
+3. In all other cases, if one of the arguments is `null`, false is returned.
+4. If the left and right arguments are of different types, the comparison fails.
+5. Strings are compared byte by byte.
+6. `true` is considered greater than `false`
+7. Numbers are compared with the accuracy of `1e-20`
+
+#### Example
+
+Let's take a JSON document as an example
+
+```json
+{
+ "left": [1, 2],
+ "right": [4, "Inaros"]
+}
+```
+
+and analyze the steps for executing the `lax $.left < $.right` query:
+
+1. Auto unpacking of arrays in the left and right arguments. As a result, the left argument is the sequence `1, 2` and the right argument is `4, "Iranos"`
+2. Let's take the pair `(1, 4)`. The comparison is successful as `1 < 4` is true. Set the flag `FOUND`
+3. Since the query is executed in `lax` mode and the `FOUND` flag is set, we aren't analyzing any more pairs.
+4. Since we have the `FOUND` flag set, the operator returns true.
+
+Let's take the same query in a different execution mode: `strict $.left < $.right`:
+
+1. Auto unpacking of arrays in the left and right arguments. As a result, the left argument is the sequence `1, 2` and the right argument is `4, "Iranos"`
+2. Let's take the pair `(1, 4)`. The comparison is successful as `1 < 4` is true. Set the flag `FOUND`
+3. Let's take the pair `(2, 4)`. The comparison is successful as `2 < 4` is true. Set the flag `FOUND`
+4. Let's take the pair `(1, "Iranos")`. The comparison fails as a number can't be compared with a string. Set the flag `ERROR`
+5. Let's take the pair `(2, "Iranos")`. The comparison fails as a number can't be compared with a string. Set the flag `ERROR`
+6. Since we have the `ERROR` flag set, the operator returns `null`
+
+### Predicates
+
+JsonPath supports predicates which are expressions that return a Boolean value and check a certain condition. You can use them, for example, in filters.
+
+#### `like_regex`
+
+The `like_regex` predicate lets you check if a string matches a regular expression. The syntax of regular expressions is the same as in [Hyperscan UDF](../udf/list/hyperscan.md) and [REGEXP](../syntax/expressions.md#regexp).
+
+##### Syntax
+
+```yql
+<expression> like_regex <regexp string> [flag <flag string>]
+```
+
+Where:
+
+1. `<expression>` is a JsonPath expression with strings to be checked for matching the regular expression.
+2. `<regexp string>` is a string with the regular expression.
+3. `flag <flag string>` is an optional section where `<flag string>` is a string with regular expression execution flags.
+
+Supported flags:
+
+- `i`: Disable the case sensitivity.
+
+##### Execution
+
+Before the check, the input sequence arrays are automatically unpacked.
+
+After that, for each element of the input sequence:
+
+1. A check is made to find out if a string matches a regular expression.
+2. If the element isn't a string, the `ERROR` flag is set.
+3. If the check result is true, the `FOUND` flag is set.
+4. If either the `ERROR` or `FOUND` flag is set and the query is executed in `lax` mode, no more pairs are analyzed.
+
+If the pair analysis results in:
+
+1. Setting the `ERROR` flag, the predicate returns `null`
+2. Setting the `FOUND` flag, the predicate returns `true`
+3. Otherwise, the predicate returns `false`
+
+##### Examples
+
+1. `"123456" like_regex "^[0-9]+$"` returns `true`
+2. `"123abcd456" like_regex "^[0-9]+$"` returns `false`
+3. `"Naomi Nagata" like_regex "nag"` returns `false`
+4. `"Naomi Nagata" like_regex "nag" flag "i"` returns `true`
+
+#### `starts with`
+
+The `starts with` predicate lets you check if one string is a prefix of another.
+
+##### Syntax
+
+```yql
+<string expression> starts with <prefix expression>
+```
+
+Where:
+
+1. `<string expression>` is a JsonPath expression with the string to check.
+2. `<prefix expression>` is a JsonPath expression with a prefix string.
+
+This means that the predicate will check that the `<string expression>` starts with the `<prefix expression>` string.
+
+##### Execution
+
+The first argument of the predicate must be a single string.
+
+The second argument of the predicate must be a sequence of (possibly, multiple) strings.
+
+For each element in a sequence of prefix strings:
+
+1. A check is made for whether "an element is a prefix of an input string"
+2. If the element isn't a string, the `ERROR` flag is set.
+3. If the check result is true, the `FOUND` flag is set.
+4. If either the `ERROR` or `FOUND` flag is set and the query is executed in `lax` mode, no more pairs are analyzed.
+
+If the pair analysis results in:
+
+1. Setting the `ERROR` flag, the predicate returns `null`
+2. Setting the `FOUND` flag, the predicate returns `true`
+3. Otherwise, the predicate returns `false`
+
+##### Examples
+
+1. `"James Holden" starts with "James"` returns `true`
+2. `"James Holden" starts with "Amos"` returns `false`
+
+#### `exists`
+
+The `exists` predicate lets you check whether a JsonPath expression returns at least one element.
+
+##### Syntax
+
+```yql
+exists (<expression>)
+```
+
+Where `<expression>` is the JsonPath expression to be checked. Parentheses around the expression are required.
+
+##### Execution
+
+1. The passed JsonPath expression is executed
+2. If an error occurs, the predicate returns `null`
+3. If an empty sequence is obtained as a result of the execution, the predicate returns `false`
+4. Otherwise, the predicate returns `true`
+
+##### Examples
+
+Let's take a JSON document:
+
+```json
+{
+ "profile": {
+ "name": "Josephus",
+ "surname": "Miller"
+ }
+}
+```
+
+1. `exists ($.profile.name)` returns `true`
+2. `exists ($.friends.profile.name)` returns `false`
+3. `strict exists ($.friends.profile.name)` returns `null`, because accessing non-existent object keys in `strict` mode is an error.
+
+#### `is unknown`
+
+The `is unknown` predicate lets you check if a Boolean value is `null`.
+
+##### Syntax
+
+```yql
+(<expression>) is unknown
+```
+
+Where `<expression>` is the JsonPath expression to be checked. Only expressions that return a Boolean value are allowed. Parentheses around the expression are required.
+
+##### Execution
+
+1. If the passed expression returns `null`, the predicate returns `true`
+2. Otherwise, the predicate returns `false`
+
+##### Examples
+
+1. `(1 == 2) is unknown` returns `false`. The `1 == 2` expression returned `false`, which is not `null`
+2. `(1 == "string") is unknown` returns `true`. The `1 == "string"` expression returned `null`, because strings and numbers can't be compared in JsonPath.
+
+### Filters
+
+JsonPath lets you filter values obtained during query execution.
+
+An expression in a filter must return a Boolean value.
+Before filtering, the input sequence arrays are automatically unpacked.
+
+For each element of the input sequence:
+
+1. The value of the current filtered `@` object becomes equal to the current element of the input sequence.
+2. Executing the expression in the filter
+3. If an error occurs during the expression execution, the current element of the input sequence is skipped.
+4. If the expression execution result is the only `true` value, the current element is added to the filter result.
+
+#### Example
+
+Suppose we have a JSON document describing the user's friends
+
+```json
+{
+ "friends": [
+ {
+ "name": "James Holden",
+ "age": 35,
+ "money": 500
+ },
+ {
+ "name": "Naomi Nagata",
+ "age": 30,
+ "money": 345
+ }
+ ]
+}
+```
+
+and we want to get a list of friends who are over 32 years old using a JsonPath query. To do this, you can write the following query:
+
+```yql
+$.friends ? (@.age > 32)
+```
+
+Let's analyze the query in parts:
+
+- `$.friends` accesses the `friends` array in the JSON document.
+- `? ( ... )` is the filter syntax. An expression inside the parentheses is called a predicate.
+- `` accesses the currently filtered object. In our example, it's the object describing a friend of the user.
+- `.age` accesses the `age` field of the currently filtered object.
+- `.age > 32` compares the `age` field with the value 32. As a result of the query, only the values for which this predicate returned true remain.
+
+The query only returns the first friend from the array of user's friends.
+
+Like many other JsonPath operators, filters can be arranged in chains. Let's take a more complex query that selects the names of friends who are older than 20 and have less than 400 currency units:
+
+```yql
+$.friends ? (@.age > 20) ? (@.money < 400) . name
+```
+
+Let's analyze the query in parts:
+
+- `$.friends` accesses the `friends` array in the JSON document.
+- `? (@.age > 20)` is the first filter. Since all friends are over 20, it just returns all the elements of the `friends` array.
+- `? (@.money < 400)` is the second filter. It only returns the second element of the `friends` array, since only its `money` field value is less than 400.
+- `.name` accesses the `name` field of filtered objects.
+
+The query returns a sequence of a single element: `"Naomi Nagata"`.
+
+In practice, it's recommended to combine multiple filters into one if possible. The above query is equivalent to `$.friends ? (@.age > 20 && @.money < 400) . name`.
+
+### Methods
+
+JsonPath supports methods that are functions converting one sequence of values to another. The syntax for calling a method is similar to accessing the object key:
+
+```yql
+$.friends.size()
+```
+
+Just like in the case of accessing object keys, method calls can be arranged in chains:
+
+```yql
+$.numbers.double().floor()
+```
+
+#### `type`
+
+The `type` method returns a string with the type of the passed value.
+
+For each element of the input sequence, the method adds this string to the output sequence according to the table below:
+
+| Value type | String with type |
+| ------------------ | ------------------------ |
+| Null | `"null"` |
+| Boolean value | `"boolean"` |
+| Number | `"number"` |
+| String | `"string"` |
+| Array | `"array"` |
+| Object | `"object"` |
+
+##### Examples
+
+1. `"Naomi".type()` returns `"string"`
+2. `false.type()` returns `"boolean"`
+
+#### `size`
+
+The `size` method returns the size of the array.
+
+For each element of the input sequence, the method adds the following to the output sequence:
+
+1. The size of the array if the element type is an array.
+2. For all other types (including objects), it adds `1`
+
+##### Examples
+
+Let's take a JSON document:
+
+```json
+{
+ "array": [1, 2, 3],
+ "object": {
+ "a": 1,
+ "b": 2
+ },
+ "scalar": "string"
+}
+```
+
+And queries to it:
+
+1. `$.array.size()` returns `3`
+2. `$.object.size()` returns `1`
+3. `$.scalar.size()` returns `1`
+
+#### `Double`
+
+The `double` method converts strings to numbers.
+
+Before its execution, the input sequence arrays are automatically unpacked.
+
+All elements in the input sequence must be strings that contain decimal numbers. It's allowed to specify the fractional part and exponent.
+
+##### Examples
+
+1. `"125".double()` returns `125`
+2. `"125.456".double()` returns `125.456`
+3. `"125.456e-3".double()` returns `0.125456`
+
+#### `ceiling`
+
+The `ceiling` method rounds up a number.
+
+Before its execution, the input sequence arrays are automatically unpacked.
+
+All elements in the input sequence must be numbers.
+
+##### Examples
+
+1. `(1.3).ceiling()` returns `2`
+2. `(1.8).ceiling()` returns `2`
+3. `(1.5).ceiling()` returns `2`
+4. `(1.0).ceiling()` returns `1`
+
+#### `floor`
+
+The `floor` method rounds down a number.
+
+Before its execution, the input sequence arrays are automatically unpacked.
+
+All elements in the input sequence must be numbers.
+
+##### Examples
+
+1. `(1.3).floor()` returns `1`
+2. `(1.8).floor()` returns `1`
+3. `(1.5).floor()` returns `1`
+4. `(1.0).floor()` returns `1`
+
+#### `abs`
+
+The `abs` method calculates the absolute value of a number (removes the sign).
+
+Before its execution, the input sequence arrays are automatically unpacked.
+
+All elements in the input sequence must be numbers.
+
+##### Examples
+
+1. `(0.0).abs()` returns `0`
+2. `(1.0).abs()` returns `1`
+3. `(-1.0).abs()` returns `1`
+
+#### `keyvalue`
+
+The `keyvalue` method converts an object to a sequence of key-value pairs.
+
+Before its execution, the input sequence arrays are automatically unpacked.
+
+All elements in the input sequence must be objects.
+
+For each element of the input sequence:
+
+1. Each key-value pair in the element is analyzed.
+2. For each key-value pair, an object is generated with the keys `name` and `value`.
+3. `name` stores a string with the name of the key from the pair.
+4. `value` stores the value from the pair.
+5. All objects for this element are added to the output sequence.
+
+##### Examples
+
+Let's take a JSON document:
+
+```json
+{
+ "name": "Chrisjen",
+ "surname": "Avasarala",
+ "age": 70
+}
+```
+
+The `$.keyvalue()` query returns the following sequence for it:
+
+```json
+{
+ "name": "age",
+ "value": 70
+},
+{
+ "name": "name",
+ "value": "Chrisjen"
+},
+{
+ "name": "surname",
+ "value": "Avasarala"
+}
+```
+
+### Variables
+
+Functions using JsonPath can pass values into a query. They are called variables. To access a variable, write the `$` character and the variable name: `$variable`.
+
+#### Example
+
+Let the `planet` variable be equal to
+
+```json
+{
+ "name": "Mars",
+ "gravity": 0.376
+}
+```
+
+Then the `strict $planet.name` query returns `"Mars"`.
+
+Unlike many programming languages, JsonPath doesn't support creating new variables or modifying existing ones.
+
+## Common arguments
+
+All functions for JSON accept:
+
+1. A JSON value (can be an arbitrary `Json` or `Json?` expression)
+2. A JsonPath query (must be explicitly specified with a string literal)
+3. **(Optional)** `PASSING` section
+
+### PASSING section
+
+Lets you pass values to a JsonPath query as variables.
+
+#### Syntax
+
+```yql
+PASSING
+ <expression 1> AS <variable name 1>,
+ <expression 2> AS <variable name 2>,
+ ...
+```
+
+`<expression>` can have the following types:
+
+- Numbers, `Date`, `DateTime`, and `Timestamp` (a `CAST` into `Double` will be made before passing a value to JsonPath)
+- `Utf8`, `Bool`, and `Json`
+
+You can set a `<variable name>` in several ways:
+
+- As an SQL name like `variable`
+- In quotes, for example, `"variable"`
+
+##### Example
+
+```yql
+JSON_VALUE(
+ $json,
+ "$.timestamp - $Now + $Hour"
+ PASSING
+ 24 * 60 as Hour,
+ CurrentUtcTimestamp() as "Now"
+)
+```
+
+## JSON_EXISTS {#json_exists}
+
+The `JSON_EXISTS` function checks if a JSON value meets the specified JsonPath.
+
+### Syntax
+
+```yql
+JSON_EXISTS(
+ <JSON expression>,
+ <JsonPath query>,
+ [<PASSING clause>]
+ [{TRUE | FALSE | UNKNOWN | ERROR} ON ERROR]
+)
+```
+
+Return value: `Bool?`
+
+Default value: If the `ON ERROR` section isn't specified, the used section is `FALSE ON ERROR`
+
+Behavior
+
+1. If `<JSON expression>` is `NULL` or an empty `Json?`, it returns an empty `Bool?`
+2. If an error occurs during JsonPath execution, the returned value depends on the `ON ERROR` section:
+
+ - `TRUE`: Return `True`
+ - `FALSE`: Return `False`
+ - `UNKNOWN`: Return an empty `Bool?`
+ - `ERROR`: Abort the entire query
+
+3. If the result of JsonPath execution is one or more values, the return value is `True`.
+4. Otherwise, `False` is returned.
+
+#### Examples
+
+```yql
+$json = CAST(@@{
+ "title": "Rocinante",
+ "crew": [
+ "James Holden",
+ "Naomi Nagata",
+ "Alex Kamai",
+ "Amos Burton"
+ ]
+}@@ as Json);
+
+SELECT
+ JSON_EXISTS($json, "$.title"), -- True
+ JSON_EXISTS($json, "$.crew[*]"), -- True
+ JSON_EXISTS($json, "$.nonexistent"); -- False, as JsonPath returns an empty result
+
+SELECT
+ -- JsonPath error, False is returned because the default section used is FALSE ON ERROR
+ JSON_EXISTS($json, "strict $.nonexistent");
+
+SELECT
+ -- JsonPath error, the entire YQL query fails.
+ JSON_EXISTS($json, "strict $.nonexistent" ERROR ON ERROR);
+```
+
+## JSON_VALUE {#json_value}
+
+The `JSON_VALUE` function retrieves a scalar value from JSON (anything that isn't an array or object).
+
+### Syntax
+
+```yql
+JSON_VALUE(
+ <JSON expression>,
+ <JsonPath query>,
+ [<PASSING clause>]
+ [RETURNING <type>]
+ [{ERROR | NULL | DEFAULT <expr>} ON EMPTY]
+ [{ERROR | NULL | DEFAULT <expr>} ON ERROR]
+)
+```
+
+Return value: `<type>?`
+
+Default values:
+
+1. If the `ON EMPTY` section isn't specified, the section used is `NULL ON EMPTY`
+2. If the `ON ERROR` section isn't specified, the section used is `NULL ON ERROR`
+3. If the `RETURNING` section isn't specified, then for `<type>`, the type used is `Utf8`
+
+Behavior:
+
+1. If `<JSON expression>` is `NULL` or an empty `Json?`, it returns an empty `<type>?`
+2. If an error occurs, the returned value depends on the `ON ERROR` section:
+
+ - `NULL`: Return an empty `<type>?`
+ - `ERROR`: Abort the entire query
+ - `DEFAULT <expr>`: Return `<expr>` after running the `CAST` function to convert the data type to `<type>?`. If the `CAST` fails, the entire query fails, too.
+
+3. If the JsonPath execution result is empty, the returned value depends on the `ON EMPTY` section:
+
+ - `NULL`: Return an empty `<type>?`
+ - `ERROR`: Abort the entire query
+ - `DEFAULT <expr>`: Return `<expr>` after running the `CAST` function to convert the data type to `<type>?`. If the `CAST` fails, the behavior matches the `ON ERROR` section.
+
+4. If the result of JsonPath execution is a single value, then:
+
+ - If the `RETURNING` section isn't specified, the value is converted to `Utf8`.
+ - Otherwise, the `CAST` function is run to convert the value to `<type>`. If the `CAST` fails, the behavior matches the `ON ERROR` section. In this case, the value from JSON must match the `<type>` type.
+
+5. Return the result
+
+Correlation between JSON and YQL types:
+
+- JSON Number: Numeric types, `Date`, `DateTime`, and `Timestamp`
+- JSON Bool: `Bool`
+- JSON String: `Utf8` and `String`
+
+Errors executing `JSON_VALUE` are as follows:
+
+- Errors evaluating JsonPath
+- The result of JsonPath execution is a number of values or a non-scalar value.
+- The type of value returned by JSON doesn't match the expected one.
+
+`The RETURNING` section supports such types as numbers, `Date`, `DateTime`, `Timestamp`, `Utf8`, `String`, and `Bool`.
+
+### Examples
+
+```yql
+$json = CAST(@@{
+ "friends": [
+ {
+ "name": "James Holden",
+ "age": 35
+ },
+ {
+ "name": "Naomi Nagata",
+ "age": 30
+ }
+ ]
+}@@ as Json);
+
+SELECT
+ JSON_VALUE($json, "$.friends[0].age"), -- "35" (type Utf8?)
+ JSON_VALUE($json, "$.friends[0].age" RETURNING Uint64), -- 35 (type Uint64?)
+ JSON_VALUE($json, "$.friends[0].age" RETURNING Utf8); -- an empty Utf8? due to an error. The JSON's Number type doesn't match the string Utf8 type.
+
+SELECT
+ -- "empty" (type String?)
+ JSON_VALUE(
+ $json,
+ "$.friends[50].name"
+ RETURNING String
+ DEFAULT "empty" ON EMPTY
+ );
+
+SELECT
+ -- 20 (type Uint64?). The result of JsonPath execution is empty, but the
+ -- default value from the ON EMPTY section can't be cast to Uint64.
+ -- That's why the value from ON ERROR is used.
+ JSON_VALUE(
+ $json,
+ "$.friends[50].age"
+ RETURNING Uint64
+ DEFAULT -1 ON EMPTY
+ DEFAULT 20 ON ERROR
+ );
+```
+
+## JSON_QUERY {#json_query}
+
+The `JSON_QUERY` function lets you retrieve arrays and objects from JSON.
+
+### Syntax
+
+```yql
+JSON_QUERY(
+ <JSON expression>,
+ <JsonPath query>,
+ [<PASSING clause>]
+ [WITHOUT [ARRAY] | WITH [CONDITIONAL | UNCONDITIONAL] [ARRAY] WRAPPER]
+ [{ERROR | NULL | EMPTY ARRAY | EMPTY OBJECT} ON EMPTY]
+ [{ERROR | NULL | EMPTY ARRAY | EMPTY OBJECT} ON ERROR]
+)
+```
+
+Return value: `Json?`
+
+Default values:
+
+1. If the `ON EMPTY` section isn't specified, the section used is `NULL ON EMPTY`
+2. If the `ON ERROR` section isn't specified, the section used is `NULL ON ERROR`
+3. If the `WRAPPER` section isn't specified, the section used is `WITHOUT WRAPPER`
+4. If the `WITH WRAPPER` section is specified but `CONDITIONAL` or `UNCONDITIONAL` is omitted, then the section used is `UNCONDITIONAL`
+
+Behavior:
+
+{% note info %}
+
+You can't specify the `WITH ... WRAPPER` and `ON EMPTY` sections at the same time.
+
+{% endnote %}
+
+1. If `<JSON expression>` is `NULL` or an empty `Json?`, it returns an empty `Json?`
+2. If the `WRAPPER` section is specified, then:
+
+ - `WITHOUT WRAPPER` or `WITHOUT ARRAY WRAPPER`: Don't convert the result of JsonPath execution in any way.
+ - `WITH UNCONDITIONAL WRAPPER` or `WITH UNCONDITIONAL ARRAY WRAPPER`: Wrap the result of JsonPath execution in an array.
+ - `WITH CONDITIONAL WRAPPER` or `WITH CONDITIONAL ARRAY WRAPPER`: Wrap the result of JsonPath execution in an array if it isn't the only array or object.
+
+3. If the JsonPath execution result is empty, the returned value depends on the `ON EMPTY` section:
+
+ - `NULL`: Return an empty `Json?`
+ - `ERROR`: Abort the entire query
+ - `EMPTY ARRAY`: Return an empty JSON array, `[]`
+ - `EMPTY OBJECT`: Return an empty JSON object, `{}`
+
+4. If an error occurs, the returned value depends on the `ON ERROR` section:
+
+ - `NULL`: Return an empty `Json?`
+ - `ERROR`: Abort the entire query
+ - `EMPTY ARRAY`: Return an empty JSON array, `[]`
+ - `EMPTY OBJECT`: Return an empty JSON object, `{}`
+
+5. Return the result
+
+Errors running a `JSON_QUERY`:
+
+- Errors evaluating JsonPath
+- The result of JsonPath execution is a number of values (even after using the `WRAPPER` section) or a scalar value.
+
+#### Examples
+
+```yql
+$json = CAST(@@{
+ "friends": [
+ {
+ "name": "James Holden",
+ "age": 35
+ },
+ {
+ "name": "Naomi Nagata",
+ "age": 30
+ }
+ ]
+}@@ as Json);
+
+SELECT
+ JSON_QUERY($json, "$.friends[0]"); -- {"name": "James Holden", "age": 35}
+
+SELECT
+ JSON_QUERY($json, "$.friends.name" WITH UNCONDITIONAL WRAPPER); -- ["James Holden", "Naomi Nagata"]
+
+SELECT
+ JSON_QUERY($json, "$.friends[0]" WITH CONDITIONAL WRAPPER), -- {"name": "James Holden", "age": 35}
+ JSON_QUERY($json, "$.friends.name" WITH CONDITIONAL WRAPPER); -- ["James Holden", "Naomi Nagata"]
+```
+
+## See also
+
+* [{#T}](../recipes/accessing-json.md)
+* [{#T}](../recipes/modifying-json.md)
diff --git a/yql/essentials/docs/en/builtins/list.md b/yql/essentials/docs/en/builtins/list.md
new file mode 100644
index 00000000000..af8e48283ad
--- /dev/null
+++ b/yql/essentials/docs/en/builtins/list.md
@@ -0,0 +1,602 @@
+# Functions for lists
+
+## ListCreate {#list-create}
+
+Construct an empty list. The only argument specifies a string describing the data type of the list cell, or the type itself obtained using [relevant functions](types.md). YQL doesn't support lists with an unknown cell type.
+
+[Documentation for the type definition format](../types/type_string.md).
+
+### Examples
+
+```yql
+SELECT ListCreate(Tuple<String,Double?>);
+```
+
+```yql
+SELECT ListCreate(OptionalType(DataType("String")));
+```
+
+## AsList and AsListStrict {#aslist}
+
+Construct a list based on one or more arguments. The argument types must be compatible in the case of `AsList` and strictly match in the case of `AsListStrict`.
+
+### Examples
+
+```yql
+SELECT AsList(1, 2, 3, 4, 5);
+```
+
+## ListLength {#listlength}
+
+The count of items in the list.
+
+### Examples
+
+```yql
+SELECT ListLength(list_column) FROM my_table;
+```
+
+## ListHasItems
+
+Check that the list contains at least one item.
+
+### Examples
+
+```yql
+SELECT ListHasItems(list_column) FROM my_table;
+```
+
+## ListCollect {#listcollect}
+
+Convert a lazy list (it can be built by such functions as [ListFilter](#listmap), [ListMap](#listmap), [ListFlatMap](#listmap)) to an eager list. In contrast to a lazy list, where each new pass re-calculates the list contents, in an eager list the content is built at once by consuming more memory.
+
+### Examples
+
+```yql
+SELECT ListCollect(list_column) FROM my_table;
+```
+
+## ListSort, ListSortAsc, and ListSortDesc {#listsort}
+
+Sort the list. By default, the ascending sorting order is applied (`ListSort` is an alias for `ListSortAsc`).
+
+Arguments:
+
+1. List.
+2. An optional expression to get the sort key from a list element (it's the element itself by default).
+
+### Examples
+
+```yql
+SELECT ListSortDesc(list_column) FROM my_table;
+```
+
+```yql
+$list = AsList(
+ AsTuple("x", 3),
+ AsTuple("xx", 1),
+ AsTuple("a", 2)
+);
+
+SELECT ListSort($list, ($x) -> {
+ RETURN $x.1;
+});
+```
+
+{% note info %}
+
+The example used a [lambda function](../syntax/expressions.md#lambda).
+
+{% endnote %}
+
+## ListExtend and ListExtendStrict {#listextend}
+
+Sequentially join lists (concatenation of lists). The arguments can be lists, optional lists, and `NULL`.
+The types of list items must be compatible in the case of `ListExtend` and strictly match in the case of `ListExtendStrict`.
+If at least one of the lists is optional, then the result is also optional.
+If at least one argument is `NULL`, then the result type is `NULL`.
+
+
+### Examples
+
+```yql
+SELECT ListExtend(
+ list_column_1,
+ list_column_2,
+ list_column_3
+) FROM my_table;
+```
+
+## ListUnionAll {#listunionall}
+
+Sequentially join lists of structures (concatenation of lists). A field is added to the output list of structures if it exists in at least one source list, but if there is no such field in any list, it is added as NULL. In the case when a field is present in two or more lists, the output field is cast to the common type.
+
+If at least one of the lists is optional, then the result is also optional.
+
+### Examples
+
+```yql
+SELECT ListUnionAll(
+ list_column_1,
+ list_column_2,
+ list_column_3
+) FROM my_table;
+```
+
+## ListZip and ListZipAll {#listzip}
+
+Based on the input lists, build a list of pairs containing the list items with matching indexes (`List<Tuplefirst_list_element_type,second_list_element_type>`).
+
+The length of the returned list is determined by the shortest list for ListZip and the longest list for ListZipAll.
+When the shorter list is exhausted, a `NULL` value of a relevant [optional type](../types/optional.md) is paired with the elements of the longer list.
+
+### Examples
+
+```yql
+SELECT
+ ListZip(list_column_1, list_column_2, list_column_3),
+ ListZipAll(list_column_1, list_column_2)
+FROM my_table;
+```
+
+## ListEnumerate {#listenumerate}
+
+Build a list of pairs (Tuple) containing the element number and the element itself (`List<TupleUint64,list_element_type>`).
+
+### Examples
+
+```yql
+SELECT ListEnumerate(list_column) FROM my_table;
+```
+
+## ListReverse {#listreverse}
+
+Reverse the list.
+
+### Examples
+
+```yql
+SELECT ListReverse(list_column) FROM my_table;
+```
+## ListSkip {#listskip}
+
+Returns a copy of the list, skipping the specified number of its first elements.
+
+The first argument specifies the source list and the second argument specifies how many elements to skip.
+
+### Examples
+
+```yql
+SELECT
+ ListSkip(list_column, 3)
+FROM my_table;
+```
+
+## ListTake {#listtake}
+
+Returns a copy of the list containing a limited number of elements from the second list.
+
+The first argument specifies the source list and the second argument specifies the maximum number of elements to be taken from the beginning of the list.
+
+### Examples
+
+```yql
+SELECT ListTake(list_column, 3) FROM my_table;
+```
+
+## ListSample and ListSampleN {#listsample}
+
+Returns a sample without replacement from the list.
+
+- `ListSample` chooses elements independently with the specified probability.
+
+- `ListSampleN` chooses a sample of the specified size (if the length of the list is less than the sample size, returns the original list).
+
+If the probability/sample size is NULL, returns the original list.
+
+An optional argument is used to control randomness, see [documentation for `Random`](basic.md#random).
+
+### Examples
+
+```yql
+ListSample(List<T>, Double?[, U])->List<T>
+ListSample(List<T>?, Double?[, U])->List<T>?
+
+ListSampleN(List<T>, Uint64?[, U])->List<T>
+ListSampleN(List<T>?, Uint64?[, U])->List<T>?
+```
+
+```yql
+$list = AsList(1, 2, 3, 4, 5);
+
+SELECT ListSample($list, 0.5); -- [1, 2, 5]
+SELECT ListSampleN($list, 2); -- [4, 2]
+```
+
+## ListShuffle {#listshuffle}
+
+Returns a shuffled copy of the list. An optional argument is used to control randomness, see [documentation for `Random`](basic.md#random).
+
+### Examples
+
+```yql
+ListShuffle(List<T>[, U])->List<T>
+ListShuffle(List<T>?[, U])->List<T>?
+```
+
+```yql
+$list = AsList(1, 2, 3, 4, 5);
+
+SELECT ListShuffle($list); -- [1, 3, 5, 2, 4]
+```
+
+## ListIndexOf {#listindexof}
+
+Searches the list for an element with the specified value and returns its index at the first occurrence. Indexes count from 0. If such element is missing, it returns `NULL`.
+
+### Examples
+
+```yql
+SELECT
+ ListIndexOf(list_column, 123)
+FROM my_table;
+```
+
+## ListMap, ListFilter, and ListFlatMap {#listmap}
+
+Apply the function specified as the second argument to each list element. The functions differ in their returned result:
+
+* `ListMap` returns a list with results.
+* `ListFlatMap` returns a list with results, combining and expanding the first level of results (lists or optional values) for each item.
+* `ListFilter` leaves only those elements where the function returned `true`.
+
+{% note info %}
+
+In `ListFlatMap`, using optional values in function results is deprecated, use the combination of [`ListNotNull`](#listnotnull) and `ListMap` instead.
+
+{% endnote %}
+
+Arguments:
+
+1. Source list.
+2. Functions for processing list elements, such as:
+
+ * [Lambda function](../syntax/expressions.md#lambda).
+ * `Module::Function` - C++ UDF.
+
+If the source list is optional, then the output list is also optional.
+
+### Examples
+
+```yql
+$callable = Python::test(Callable<(Int64)->Bool>, "defMyFavouriteCrutchtest(i): return i % 2");
+SELECT
+ ListMap(list_column, ($x) -> { RETURN $x > 2; }),
+ ListFlatMap(list_column, My::Udf),
+ ListFilter(list_column, $callable)
+FROM my_table;
+```
+
+## ListNotNull {#listnotnull}
+
+Applies transformation to the source list, skipping empty optional items and strengthening the item type to non-optional. For a list with non-optional items, it returns the unchanged source list.
+
+If the source list is optional, then the output list is also optional.
+
+### Examples
+
+```yql
+SELECT ListNotNull([1,2]), -- [1,2]
+ ListNotNull([3,null,4]); -- [3,4]
+```
+
+## ListFlatten {#listflatten}
+
+Expands the list of lists into a flat list, preserving the order of items. As the top-level list item you can use an optional list that is interpreted as an empty list in the case of `NULL`.
+
+If the source list is optional, then the output list is also optional.
+
+### Examples
+
+```yql
+SELECT ListFlatten([[1,2],[3,4]]), -- [1,2,3,4]
+ ListFlatten([null,[3,4],[5,6]]); -- [3,4,5,6]
+```
+
+## ListUniq {#listuniq}
+
+Returns a copy of the list containing only distinct elements.
+
+### Examples
+
+```yql
+SELECT
+ ListUniq(list_column)
+FROM my_table;
+```
+
+## ListAny and ListAll {#listany}
+
+Returns `true` for a list of Boolean values, if:
+
+* `ListAny`: At least one element is `true`.
+* `ListAll`: All elements are `true`.
+
+Otherwise, it returns false.
+
+### Examples
+
+```yql
+SELECT
+ ListAll(bool_column),
+ ListAny(bool_column)
+FROM my_table;
+```
+
+## ListHas {#listhas}
+
+Show whether the list contains the specified element. In this case, `NULL` values are considered equal to each other, and with a `NULL` input list, the result is always `false`.
+
+### Examples
+
+```yql
+SELECT
+ ListHas(list_column, "my_needle")
+FROM my_table;
+```
+
+## ListHead, ListLast {#listheadlast}
+
+Returns the first and last item of the list.
+
+### Examples
+
+```yql
+SELECT
+ ListHead(numeric_list_column) AS head,
+ ListLast(numeric_list_column) AS last
+FROM my_table;
+```
+
+## ListMin, ListMax, ListSum and ListAvg {#listminy}
+
+Apply the appropriate aggregate function to all elements of the numeric list.
+
+### Examples
+
+```yql
+SELECT
+ ListMax(numeric_list_column) AS max,
+ ListMin(numeric_list_column) AS min,
+ ListSum(numeric_list_column) AS sum,
+ ListAvg(numeric_list_column) AS avg
+FROM my_table;
+```
+
+## ListFold, ListFold1 {#listfold}
+
+Folding a list.
+
+Arguments:
+
+1. List
+2. Initial state `U` for `ListFold`, `initLambda(item:T)->U` for `ListFold1`
+3. `updateLambda(item:T, state:U)->U`
+
+Type returned:
+`U` for `ListFold`, `U?` for `ListFold1`.
+
+```yql
+$l = [1, 4, 7, 2];
+$y = ($x, $y) -> { RETURN $x + $y; };
+$z = ($x) -> { RETURN 4 * $x; };
+
+SELECT
+ ListFold($l, 6, $y) AS fold, -- 20
+ ListFold([], 3, $y) AS fold_empty, -- 3
+ ListFold1($l, $z, $y) AS fold1, -- 17
+ ListFold1([], $z, $y) AS fold1_empty; -- Null
+```
+
+## ListFoldMap, ListFold1Map {#listfoldmap}
+
+Converts each list item i by calling the handler(i, state).
+
+Arguments:
+
+1. List
+2. Initial state `S` for `ListFoldMap`, `initLambda(item:T)->tuple (U S)` for `ListFold1Map`
+3. `handler(item:T, state:S)->tuple (U S)`
+
+Type returned: `List` of `U` items.
+
+### Examples
+
+```yql
+$l = [1, 4, 7, 2];
+$x = ($i, $s) -> { RETURN ($i * $s, $i + $s); };
+$t = ($i) -> { RETURN ($i + 1, $i + 2); };
+
+SELECT
+ ListFoldMap([], 1, $x), -- []
+ ListFoldMap($l, 1, $x), -- [1, 8, 42, 26]
+ ListFold1Map([], $t, $x), -- []
+ ListFold1Map($l, $t, $x); -- [2, 12, 49, 28]
+```
+
+## ListFromRange {#listfromrange}
+
+Generate a sequence of numbers with the specified step. It's similar to `xrange` in Python 2, but additionally supports floats.
+
+Arguments:
+
+1. Start
+2. End
+3. Step (optional, 1 by default)
+
+Specifics:
+
+* The end is not included, i.e. `ListFromRange(1,3) == AsList(1,2)`.
+* The type for the resulting elements is selected as the broadest from the argument types. For example, `ListFromRange(1, 2, 0.5)` results in a `Double` list.
+* If the start and the end is one of the date representing type, the step has to be `Interval`.
+* The list is "lazy", but if it's used incorrectly, it can still consume a lot of RAM.
+* If the step is positive and the end is less than or equal to the start, the result list is empty.
+* If the step is negative and the end is greater than or equal to the start, the result list is empty.
+* If the step is neither positive nor negative (0 or NaN), the result list is empty.
+* If any of the parameters is optional, the result list is optional.
+* If any of the parameters is `NULL`, the result is `NULL`.
+
+### Examples
+
+```yql
+SELECT
+ ListFromRange(-2, 2), -- [-2, -1, 0, 1]
+ ListFromRange(2, 1, -0.5); -- [2.0, 1.5]
+```
+
+### Signature
+
+```yql
+ListFromRange(T{Flags:AutoMap}, T{Flags:AutoMap}, T?)->LazyList<T> -- T — numeric type
+ListFromRange(T{Flags:AutoMap}, T{Flags:AutoMap}, I?)->LazyList<T> -- T — type, representing date/time, I — interval
+```
+
+## ListReplicate {#listreplicate}
+
+Creates a list containing multiple copies of the specified value.
+
+Required arguments:
+
+1. Value.
+2. Number of copies.
+
+### Examples
+
+```yql
+SELECT ListReplicate(true, 3); -- [true, true, true]
+```
+
+## ListConcat {#listconcat}
+
+Concatenates a list of strings into a single string.
+You can set a separator as the second parameter.
+
+### Examples
+
+```yql
+SELECT
+ ListConcat(string_list_column),
+ ListConcat(string_list_column, "; ")
+FROM my_table;
+```
+
+## ListExtract {#listextract}
+
+For a list of structures, it returns a list of contained fields having the specified name.
+
+### Examples
+
+```yql
+SELECT
+ ListExtract(struct_list_column, "MyMember")
+FROM my_table;
+```
+
+## ListTakeWhile, ListSkipWhile {#listtakewhile}
+
+`ListTakeWhile` returns a list from the beginning while the predicate is true, then the list ends.
+
+`ListSkipWhile` skips the list segment from the beginning while the predicate is true, then returns the rest of the list ignoring the predicate.
+`ListTakeWhileInclusive` returns a list from the beginning while the predicate is true. Then the list ends, but it also includes the item on which the stopping predicate triggered.
+`ListSkipWhileInclusive` skips a list segment from the beginning while the predicate is true, then returns the rest of the list disregarding the predicate, but excluding the element that matched the predicate and starting with the next element after it.
+
+Required arguments:
+
+1. List.
+2. Predicate.
+
+If the input list is optional, then the result is also optional.
+
+### Examples
+
+```yql
+$data = AsList(1, 2, 5, 1, 2, 7);
+
+SELECT
+ ListTakeWhile($data, ($x) -> {return $x <= 3}), -- [1, 2]
+ ListSkipWhile($data, ($x) -> {return $x <= 3}), -- [5, 1, 2, 7]
+ ListTakeWhileInclusive($data, ($x) -> {return $x <= 3}), -- [1, 2, 5]
+ ListSkipWhileInclusive($data, ($x) -> {return $x <= 3}); -- [1, 2, 7]
+```
+
+## ListAggregate {#listaggregate}
+
+Apply the [aggregation factory](basic.md#aggregationfactory) to the passed list.
+If the passed list is empty, the aggregation result is the same as for an empty table: 0 for the `COUNT` function and `NULL` for other functions.
+If the passed list is optional and `NULL`, the result is also `NULL`.
+
+Arguments:
+
+1. List.
+2. [Aggregation factory](basic.md#aggregationfactory).
+
+### Examples
+
+```yql
+SELECT ListAggregate(AsList(1, 2, 3), AggregationFactory("Sum")); -- 6
+```
+
+## ToDict and ToMultiDict {#todict}
+
+Convert a list of tuples containing key-value pairs to a dictionary. In case of conflicting keys in the input list, `ToDict` leaves the first value and `ToMultiDict` builds a list of all the values.
+
+It means that:
+
+* `ToDict` converts `List<TupleK, V="">` to `Dict<K, V="">`
+* `ToMultiDict` converts `List<TupleK, V>` to `Dict<K, List<V>>`
+
+Optional lists are also supported, resulting in an optional dictionary.
+
+### Examples
+
+```yql
+SELECT
+ ToDict(tuple_list_column)
+FROM my_table;
+```
+
+## ToSet {#toset}
+
+Converts a list to a dictionary where the keys are unique elements of this list, and values are omitted and have the type `Void`. For the `List<T>` list, the result type is `Dict<T, Void="">`.
+An optional list is also supported, resulting in an optional dictionary.
+
+Inverse function: get a list of keys for the [DictKeys](dict.md#dictkeys) dictionary.
+
+### Examples
+
+```yql
+SELECT
+ ToSet(list_column)
+FROM my_table;
+```
+
+## ListTop, ListTopAsc, ListTopDesc, ListTopSort, ListTopSortAsc и ListTopSortDesc {#listtop}
+
+Select top values from the list. `ListTopSort*` additionally sorts the returned values. The smallest values are selected by default. Thus, the functions without a suffix are the aliases to `*Asc` functions, while `*Desc` functions return the largest values.
+
+`ListTopSort` is more effective than consecutive `ListTop` and `ListSort` because `ListTop` can partially sort the list to find needed values. However, `ListTop` is more effective than `ListTopSort` when the result order is unimportant.
+
+Arguments:
+
+1. List.
+2. Size of selection.
+3. An optional expression to get the sort key from a list element (it's the element itself by default).
+
+### Examples
+
+```yql
+ListTop(List<T>{Flags:AutoMap}, N)->List<T>
+ListTop(List<T>{Flags:AutoMap}, N, (T)->U)->List<T>
+```
+
+The signatures of other functions are the same.
diff --git a/yql/essentials/docs/en/builtins/struct.md b/yql/essentials/docs/en/builtins/struct.md
new file mode 100644
index 00000000000..9ab3442d728
--- /dev/null
+++ b/yql/essentials/docs/en/builtins/struct.md
@@ -0,0 +1,359 @@
+# Functions for structures
+
+## TryMember {#trymember}
+
+Trying to get a field from the structure. If it's not found among the fields or null in the structure value, use the default value.
+
+Arguments:
+
+1. Structure.
+2. Field name.
+3. Default value.
+
+```yql
+$struct = <|a:1|>;
+SELECT
+ TryMember(
+ $struct,
+ "a",
+ 123
+ ) AS a, -- 1
+ TryMember(
+ $struct,
+ "b",
+ 123
+ ) AS b; -- 123
+```
+
+## ExpandStruct {#expandstruct}
+
+Adding one or more new fields to the structure.
+
+If the field set contains duplicate values, an error is returned.
+
+Arguments:
+
+* The first argument passes the source structure to be expanded.
+* All the other arguments must be named, each argument adds a new field and the argument's name is used as the field's name (as in [AsStruct](basic.md#asstruct)).
+
+### Examples
+
+```yql
+$struct = <|a:1|>;
+SELECT
+ ExpandStruct(
+ $struct,
+ 2 AS b,
+ "3" AS c
+ ) AS abc;
+```
+
+## AddMember {#addmember}
+
+Adding one new field to the structure. If you need to add multiple fields, better use [ExpandStruct](#expandstruct).
+
+If the field set contains duplicate values, an error is returned.
+
+Arguments:
+
+1. Source structure.
+2. Name of the new field.
+3. Value of the new field.
+
+### Examples
+
+```yql
+$struct = <|a:1|>;
+SELECT
+ AddMember(
+ $struct,
+ "b",
+ 2
+ ) AS ab;
+```
+
+## RemoveMember {#removemember}
+
+Removing a field from the structure.
+
+If the entered field hasn't existed, an error is returned.
+
+Arguments:
+
+1. Source structure.
+2. Field name.
+
+### Examples
+
+```yql
+$struct = <|a:1, b:2|>;
+SELECT
+ RemoveMember(
+ $struct,
+ "b"
+ ) AS a;
+```
+
+## ForceRemoveMember {#forceremovemember}
+
+Removing a field from the structure.
+
+If the entered field hasn't existed, unlike [RemoveMember](#removemember), the error is not returned.
+
+Arguments:
+
+1. Source structure.
+2. Field name.
+
+### Examples
+
+```yql
+$struct = <|a:1, b:2|>;
+SELECT
+ ForceRemoveMember(
+ $struct,
+ "c"
+ ) AS ab;
+```
+
+## ChooseMembers {#choosemembers}
+
+Selecting fields with specified names from the structure.
+
+If any of the fields haven't existed, an error is returned.
+
+Arguments:
+
+1. Source structure.
+2. List of field names.
+
+### Examples
+
+```yql
+$struct = <|a:1, b:2, c:3|>;
+SELECT
+ ChooseMembers(
+ $struct,
+ ["a", "b"]
+ ) AS ab;
+```
+
+## RemoveMembers {#removemembers}
+
+Excluding fields with specified names from the structure.
+
+If any of the fields haven't existed, an error is returned.
+
+Arguments:
+
+1. Source structure.
+2. List of field names.
+
+### Examples
+
+```yql
+$struct = <|a:1, b:2, c:3|>;
+SELECT
+ RemoveMembers(
+ $struct,
+ ["a", "b"]
+ ) AS c;
+```
+
+## ForceRemoveMembers {#forceremovemembers}
+
+Excluding fields with specified names from the structure.
+
+If any of the fields haven't existed, it is ignored.
+
+Arguments:
+
+1. Source structure.
+2. List of field names.
+
+### Examples
+
+```yql
+$struct = <|a:1, b:2, c:3|>;
+SELECT
+ ForceRemoveMembers(
+ $struct,
+ ["a", "b", "z"]
+ ) AS c;
+```
+
+## CombineMembers {#combinemembers}
+
+Combining the fields from multiple structures into a new structure.
+
+If the resulting field set contains duplicate values, an error is returned.
+
+Arguments: two or more structures.
+
+### Examples
+
+```yql
+$struct1 = <|a:1, b:2|>;
+$struct2 = <|c:3|>;
+SELECT
+ CombineMembers(
+ $struct1,
+ $struct2
+ ) AS abc;
+```
+
+## FlattenMembers {#flattenmembers}
+
+Combining the fields from multiple new structures into another new structure with prefix support.
+
+If the resulting field set contains duplicate values, an error is returned.
+
+Arguments: two or more tuples of two items: prefix and structure.
+
+### Examples
+
+```yql
+$struct1 = <|a:1, b:2|>;
+$struct2 = <|c:3|>;
+SELECT
+ FlattenMembers(
+ AsTuple("foo", $struct1), -- fooa, foob
+ AsTuple("bar", $struct2) -- barc
+ ) AS abc;
+```
+
+## StructMembers {#structmembers}
+
+Returns an unordered list of field names (possibly removing one Optional level) for a single argument that is a structure. For the `NULL` argument, an empty list of strings is returned.
+
+Argument: structure
+
+### Examples
+
+```yql
+$struct = <|a:1, b:2|>;
+SELECT
+ StructMembers($struct); -- ['a', 'b']
+```
+
+## RenameMembers {#renamemembers}
+
+Renames the fields in the structure passed. In this case, you can rename a source field into multiple target fields. All fields not mentioned in the renaming as source names are moved to the result structure. If some source field is omitted in the rename list, an error is returned. For an Optional structure or `NULL`, the result has the same type.
+
+Arguments:
+
+1. Source structure.
+2. A tuple of field names: the original name, the new name.
+
+### Examples
+
+```yql
+$struct = <|a:1, b:2|>;
+SELECT
+ RenameMembers($struct, [('a', 'c'), ('a', 'e')]); -- (b:2, c:1, e:1)
+```
+
+## ForceRenameMembers {#forecerenamemembers}
+
+Renames the fields in the structure passed. In this case, you can rename a source field into multiple target fields. All fields not mentioned in the renaming as source names are moved to the result structure. If some source field is omitted in the rename list, the name is ignored. For an Optional structure or `NULL`, the result has the same type.
+
+Arguments:
+
+1. Source structure.
+2. A tuple of field names: the original name, the new name.
+
+### Examples
+
+```yql
+$struct = <|a:1, b:2|>;
+SELECT
+ ForceRenameMembers($struct, [('a', 'c'), ('d', 'e')]); -- (b:2, c:1)
+```
+
+## GatherMembers {#gathermembers}
+
+Returns an unordered list of tuples including the field name and value. For the `NULL` argument, `EmptyList` is returned. It can be used only in the cases when the types of items in the structure are the same or compatible. Returns an optional list for an optional structure.
+
+Argument: structure
+
+### Examples
+
+```yql
+$struct = <|a:1, b:2|>;
+SELECT
+ GatherMembers($struct); -- [('a', 1), ('b', 2)]
+```
+
+## SpreadMembers {#spreadmembers}
+
+Creates a structure with a specified list of fields and applies a specified list of edits to it in the format (field name, field value). All types of fields in the resulting structure are the same and equal to the type of values in the update list with added Optional (unless they are optional already). If the field wasn't mentioned among the list of updated fields, it's returned as `NULL`. Among all updates for a field, the latest one is written. If the update list is Optional or `NULL`, the result has the same type. If the list of edits includes a field that is not in the list of expected fields, an error is returned.
+
+Arguments:
+
+1. List of tuples: field name, field value.
+2. A list of all possible field names in the structure.
+
+### Examples
+
+```yql
+SELECT
+ SpreadMembers([('a',1),('a',2)],['a','b']); -- (a: 2, b: null)
+```
+
+## ForceSpreadMembers {#forcespreadmembers}
+
+Creates a structure with a specified list of fields and applies to it the specified list of updates in the format (field name, field value). All types of fields in the resulting structure are the same and equal to the type of values in the update list with added Optional (unless they are optional already). If the field wasn't mentioned among the list of updated fields, it's returned as `NULL`. Among all updates for a field, the latest one is written. If the update list is optional or equal to `NULL`, the result has the same type. If the list of updates includes a field that is not in the list of expected fields, this edit is ignored.
+
+Arguments:
+
+1. List of tuples: field name, field value.
+2. A list of all possible field names in the structure.
+
+### Examples
+
+```yql
+SELECT
+ ForceSpreadMembers([('a',1),('a',2),('c',100)],['a','b']); -- (a: 2, b: null)
+```
+
+## StructUnion, StructIntersection, StructDifference, StructSymmetricDifference
+
+Combine two structures using one of the four methods (using the provided lambda to merge fields with the same name):
+
+* `StructUnion` adds all fields of both of the structures to the result.
+* `StructIntersection` adds only the fields which are present in both of the structures.
+* `StructDifference` adds only the fields of `left`, which are absent in `right`.
+* `StructSymmetricDifference` adds all fields that are present in exactly one of the structures.
+
+### Signatures
+
+```yql
+StructUnion(left:Struct<...>, right:Struct<...>[, mergeLambda:(name:String, l:T1?, r:T2?)->T])->Struct<...>
+StructIntersection(left:Struct<...>, right:Struct<...>[, mergeLambda:(name:String, l:T1?, r:T2?)->T])->Struct<...>
+StructDifference(left:Struct<...>, right:Struct<...>)->Struct<...>
+StructSymmetricDifference(left:Struct<...>, right:Struct<...>)->Struct<...>
+```
+
+Arguments:
+
+1. `left` - first structure.
+2. `right` - second structure.
+3. `mergeLambda` - _(optional)_ function to merge fields with the same name (arguments: field name, `Optional` field value of the first struct, `Optional` field value of the second struct - arguments are `Nothing<T?>` in case of absence of the corresponding struct field). By default, if present, the first structure's field value is used; otherwise, the second one's value is used.
+
+### Examples
+
+```yql
+$merge = ($name, $l, $r) -> {
+ return ($l ?? 0) + ($r ?? 0);
+};
+$left = <|a: 1, b: 2, c: 3|>;
+$right = <|c: 1, d: 2, e: 3|>;
+
+SELECT
+ StructUnion($left, $right), -- <|a: 1, b: 2, c: 3, d: 2, e: 3|>
+ StructUnion($left, $right, $merge), -- <|a: 1, b: 2, c: 4, d: 2, e: 3|>
+ StructIntersection($left, $right, $merge), -- <|c: 4|>
+ StructDifference($left, $right), -- <|a: 1, b: 1|>
+ StructSymmetricDifference($left, $right) -- <|a: 1, b: 2, d: 2, e: 3|>
+;
+```
diff --git a/yql/essentials/docs/en/builtins/toc_i.yaml b/yql/essentials/docs/en/builtins/toc_i.yaml
new file mode 100644
index 00000000000..45c83c7fc07
--- /dev/null
+++ b/yql/essentials/docs/en/builtins/toc_i.yaml
@@ -0,0 +1,13 @@
+items:
+- { name: Overview, href: index.md }
+- { name: Basic, href: basic.md }
+- { name: Aggregate, href: aggregation.md }
+- { name: Window, href: window.md }
+- { name: For lists, href: list.md }
+- { name: For dictionaries, href: dict.md }
+- { name: For structures, href: struct.md }
+- { name: For types, href: types.md }
+- { name: For code generation, href: codegen.md }
+- { name: For JSON, href: json.md }
+- name: C++ libraries
+ include: { mode: link, path: ../udf/list/toc_i.yaml }
diff --git a/yql/essentials/docs/en/builtins/types.md b/yql/essentials/docs/en/builtins/types.md
new file mode 100644
index 00000000000..2b66d5528e1
--- /dev/null
+++ b/yql/essentials/docs/en/builtins/types.md
@@ -0,0 +1,637 @@
+# Functions for data types
+
+## FormatType {#formattype}
+
+Serializing a type or a handle type to a human-readable string. This helps at debugging and will also be used in the next examples of this section. [Documentation for the format](../types/type_string.md).
+
+## ParseType {#parsetype}
+
+Building a type from a string with description. [Documentation for its format](../types/type_string.md).
+
+### Examples
+
+```yql
+SELECT FormatType(ParseType("List<Int32>")); -- List<int32>
+```
+
+## TypeOf {#typeof}
+
+Getting the type of value passed to the argument.
+
+### Examples
+
+```yql
+SELECT FormatType(TypeOf("foo")); -- String
+```
+
+```yql
+SELECT FormatType(TypeOf(AsTuple(1, 1u))); -- Tuple<Int32,Uint32>
+```
+
+## InstanceOf {#instanceof}
+
+Returns an instance of the specified type that can only be used to get the type of the result of an expression that uses this type.
+
+If this instance remains in the computation graph by the end of optimization, the operation fails.
+
+### Examples
+
+```yql
+SELECT FormatType(TypeOf(
+ InstanceOf(ParseType("Int32")) +
+ InstanceOf(ParseType("Double"))
+)); -- Double, because "Int32 + Double" returns Double
+```
+
+## DataType {#datatype}
+
+Returns a type for [primitive data types](../types/primitive.md) based on type name.
+
+### Examples
+
+```yql
+SELECT FormatType(DataType("Bool")); -- Bool
+SELECT FormatType(DataType("Decimal","5","1")); -- Decimal(5,1)
+```
+
+## OptionalType {#optionaltype}
+
+Adds the option to assign `NULL` to the passed type.
+
+### Examples
+
+```yql
+SELECT FormatType(OptionalType(DataType("Bool"))); -- Bool?
+```
+
+## ListType and StreamType {#listtype}
+
+Builds a list type or stream type based on the passed element type.
+
+### Examples
+
+```yql
+SELECT FormatType(ListType(DataType("Bool"))); -- List<Bool>
+```
+
+## DictType {#dicttype}
+
+Builds a dictionary type based on the passed key types (first argument) and value types (second argument).
+
+### Examples
+
+```yql
+SELECT FormatType(DictType(
+ DataType("String"),
+ DataType("Double")
+)); -- Dict<String,Double>
+```
+
+## TupleType {#tupletype}
+
+Builds the tuple type from the passed element types.
+
+### Examples
+
+```yql
+SELECT FormatType(TupleType(
+ DataType("String"),
+ DataType("Double"),
+ OptionalType(DataType("Bool"))
+)); -- Tuple<String,Double,Bool?>
+```
+
+## StructType {#structtype}
+
+Builds the structure type based on the passed element types. The standard syntax of named arguments is used to specify the element names.
+
+### Examples
+
+```yql
+SELECT FormatType(StructType(
+ DataType("Bool") AS MyBool,
+ ListType(DataType("String")) AS StringList
+)); -- Struct<'MyBool':Bool,'StringList':List<String>>
+```
+
+## VariantType {#varianttype}
+
+Returns the type of a variant based on the underlying type (structure or tuple).
+
+### Examples
+
+```yql
+SELECT FormatType(VariantType(
+ ParseType("Struct<foo:Int32,bar:Double>")
+)); -- Variant<'bar':Double,'foo':Int32>
+```
+
+## ResourceType {#resourcetype}
+
+Returns the type of the [resource](../types/special.md) based on the passed string label.
+
+### Examples
+
+```yql
+SELECT FormatType(ResourceType("Foo")); -- Resource<'Foo'>
+```
+
+## CallableType {#callabletype}
+
+Constructs the type of the called value using the following arguments:
+
+1. Number of optional arguments (if all arguments are required — 0).
+2. Result type.
+3. All the next arguments of CallableType are treated as types of arguments of the callable value, but with a shift for two required arguments (for example, the third argument of the CallableType describes the type of the first argument in the callable value).
+
+### Examples
+
+```yql
+SELECT FormatType(CallableType(
+ 1, -- optional args count
+ DataType("Double"), -- result type
+ DataType("String"), -- arg #1 type
+ OptionalType(DataType("Int64")) -- arg #2 type
+)); -- Callable<(String,[Int64?])->Double>
+```
+
+## GenericType, UnitType, and VoidType {#generictype}
+
+Return the same-name [special data types](../types/special.md). They have no arguments because they are not parameterized.
+
+### Examples
+
+```yql
+SELECT FormatType(VoidType()); -- Void
+```
+
+## OptionalItemType, ListItemType and StreamItemType {#optionalitemtype}
+
+If a type is passed to these functions, then they perform the action reverse to [OptionalType](#optionaltype), [ListType](#listtype), and [StreamType](#listtype): return the item type based on its container type.
+
+If a type handle is passed to these functions, then they perform the action reverse to [OptionalTypeHandle](#optionaltypehandle), [ListTypeHandle](#list-stream-typehandle), and [StreamTypeHandle](#list-stream-typehandle): they return the handle of the element type based on the type handle of its container.
+
+### Examples
+
+```yql
+SELECT FormatType(ListItemType(
+ ParseType("List<Int32>")
+)); -- Int32
+```
+
+```yql
+SELECT FormatType(ListItemType(
+ ParseTypeHandle("List<Int32>")
+)); -- Int32
+```
+
+## DictKeyType and DictPayloadType {#dictkeytype}
+
+Returns the type of the key or value based on the dictionary type.
+
+### Examples
+
+```yql
+SELECT FormatType(DictKeyType(
+ ParseType("Dict<Int32,String>")
+)); -- Int32
+```
+
+## TupleElementType {#tupleelementtype}
+
+Returns the tuple's element type based on the tuple type and the element index (index starts from zero).
+
+### Examples
+
+```yql
+SELECT FormatType(TupleElementType(
+ ParseType("Tuple<Int32,Double>"), "1"
+)); -- Double
+```
+
+## StructMemberType {#structmembertype}
+
+Returns the type of the structure element based on the structure type and element name.
+
+### Examples
+
+```yql
+SELECT FormatType(StructMemberType(
+ ParseType("Struct<foo:Int32,bar:Double>"), "foo"
+)); -- Int32
+```
+
+## CallableResultType and CallableArgumentType {#callableresulttype}
+
+`CallableResultType` returns the result type based on the type of the called value. `CallableArgumentType` returns the argument type based on the called value type and its index (index starts from zero).
+
+### Examples
+
+```yql
+$callable_type = ParseType("(String,Bool)->Double");
+
+SELECT FormatType(CallableResultType(
+ $callable_type
+)), -- Double
+FormatType(CallableArgumentType(
+ $callable_type, 1
+)); -- Bool
+```
+
+## VariantUnderlyingType {#variantunderlyingtype}
+
+If a type is passed to this function, then it performs an action reverse to [VariantType](#varianttype): it returns the underlying type based on the variant type.
+
+If a type handle is passed to this function, it performs the action reverse to [VariantTypeHandle](#varianttypehandle): returns the handle of the underlying type based on the handle of the variant type.
+
+### Examples
+
+```yql
+SELECT FormatType(VariantUnderlyingType(
+ ParseType("Variant<foo:Int32,bar:Double>")
+)), -- Struct<'bar':Double,'foo':Int32>
+FormatType(VariantUnderlyingType(
+ ParseType("Variant<Int32,Double>")
+)); -- Tuple<Int32,Double>
+```
+
+```yql
+SELECT FormatType(VariantUnderlyingType(
+ ParseTypeHandle("Variant<foo:Int32,bar:Double>")
+)), -- Struct<'bar':Double,'foo':Int32>
+FormatType(VariantUnderlyingType(
+ ParseTypeHandle("Variant<Int32,Double>")
+)); -- Tuple<Int32,Double>
+```
+
+## Functions for data types during calculations
+
+To work with data types during calculations, use handle types: these are [resources](../types/special.md) that contain an opaque type definition. After constructing the type handle, you can revert to the regular type using the [EvaluateType](#evaluatetype) function. For debug purposes, you can convert a handle type to a string using the [FormatType](#formattype) function.
+
+### TypeHandle
+
+Getting a type handle from the type passed to the argument.
+
+#### Examples
+
+```yql
+SELECT FormatType(TypeHandle(TypeOf("foo"))); -- String
+```
+
+### EvaluateType
+
+Getting the type from the type handle passed to the argument. The function is evaluated before the start of the main calculation, as well as [EvaluateExpr](basic.md#evaluate_expr_atom).
+
+#### Examples
+
+```yql
+SELECT FormatType(EvaluateType(TypeHandle(TypeOf("foo")))); -- String
+```
+
+### ParseTypeHandle
+
+Building a type handle from a string with description. [Documentation for its format](../types/type_string.md).
+
+#### Examples
+
+```yql
+SELECT FormatType(ParseTypeHandle("List<Int32>")); -- List<int32>
+```
+
+### TypeKind
+
+Getting the top-level type name from the type handle passed to the argument.
+
+#### Examples
+
+```yql
+SELECT TypeKind(TypeHandle(TypeOf("foo"))); -- Data
+SELECT TypeKind(ParseTypeHandle("List<Int32>")); -- List
+```
+
+### DataTypeComponents
+
+Getting the name and parameters for a [primitive data type](../types/primitive.md) from the primitive type handle passed to the argument. Reverse function: [DataTypeHandle](#datatypehandle).
+
+#### Examples
+
+```yql
+SELECT DataTypeComponents(TypeHandle(TypeOf("foo"))); -- ["String"]
+SELECT DataTypeComponents(ParseTypeHandle("Decimal(4,1)")); -- ["Decimal", "4", "1"]
+```
+
+### DataTypeHandle
+
+Constructing a handle for a [primitive data type](../types/primitive.md) from its name and parameters passed to the argument as a list. Reverse function: [DataTypeComponents](#datatypecomponents).
+
+#### Examples
+
+```yql
+SELECT FormatType(DataTypeHandle(
+ AsList("String")
+)); -- String
+
+SELECT FormatType(DataTypeHandle(
+ AsList("Decimal", "4", "1")
+)); -- Decimal(4,1)
+```
+
+### OptionalTypeHandle
+
+Adds the option to assign `NULL` to the passed type handle.
+
+#### Examples
+
+```yql
+SELECT FormatType(OptionalTypeHandle(
+ TypeHandle(DataType("Bool"))
+)); -- Bool?
+```
+
+### PgTypeName
+
+Getting the name of the PostgreSQL type from the type handle passed to the argument. Inverse function: [PgTypeHandle](#pgtypehandle).
+
+#### Examples
+
+```yql
+SELECT PgTypeName(ParseTypeHandle("pgint4")); -- int4
+```
+
+### PgTypeHandle
+
+Builds a type handle based on the passed name of the PostgreSQL type. Inverse function: [PgTypeName](#pgtypename).
+
+#### Examples
+
+```yql
+SELECT FormatType(PgTypeHandle("int4")); -- pgint4
+```
+
+### ListTypeHandle and StreamTypeHandle {#list-stream-typehandle}
+
+Builds a list type handle or stream type handle based on the passed element type handle.
+
+#### Examples
+
+```yql
+SELECT FormatType(ListTypeHandle(
+ TypeHandle(DataType("Bool"))
+)); -- List<Bool>
+```
+
+### EmptyListTypeHandle and EmptyDictTypeHandle
+
+Constructs a handle for an empty list or dictionary.
+
+#### Examples
+
+```yql
+SELECT FormatType(EmptyListTypeHandle()); -- EmptyList
+```
+
+### TupleTypeComponents
+
+Getting a list of element type handles from the tuple type handle passed to the argument. Inverse function: [TupleTypeHandle](#tupletypehandle).
+
+#### Examples
+
+```yql
+SELECT ListMap(
+ TupleTypeComponents(
+ ParseTypeHandle("Tuple<Int32, String>")
+ ),
+ ($x)->{
+ return FormatType($x)
+ }
+); -- ["Int32", "String"]
+```
+
+### TupleTypeHandle
+
+Building a tuple type handle from handles of element types passed as a list to the argument. Inverse function: [TupleTypeComponents](#tupletypecomponents).
+
+#### Examples
+
+```yql
+SELECT FormatType(
+ TupleTypeHandle(
+ AsList(
+ ParseTypeHandle("Int32"),
+ ParseTypeHandle("String")
+ )
+ )
+); -- Tuple<Int32,String>
+```
+
+### StructTypeComponents
+
+Getting a list of element type handles and their names from the structure type handle passed to the argument. Inverse function: [StructTypeHandle](#structtypehandle).
+
+#### Examples
+
+```yql
+SELECT ListMap(
+ StructTypeComponents(
+ ParseTypeHandle("Struct<a:Int32, b:String>")
+ ),
+ ($x) -> {
+ return AsTuple(
+ FormatType($x.Type),
+ $x.Name
+ )
+ }
+); -- [("Int32","a"), ("String","b")]
+```
+
+### StructTypeHandle
+
+Building a structure type handle from handles of element types and names passed as a list to the argument. Inverse function: [StructTypeComponents](#structtypecomponents).
+
+#### Examples
+
+```yql
+SELECT FormatType(
+ StructTypeHandle(
+ AsList(
+ AsStruct(ParseTypeHandle("Int32") as Type,"a" as Name),
+ AsStruct(ParseTypeHandle("String") as Type, "b" as Name)
+ )
+ )
+); -- Struct<'a':Int32,'b':String>
+```
+
+### DictTypeComponents
+
+Getting a key-type handle and a value-type handle from the dictionary-type handle passed to the argument. Inverse function: [DictTypeHandle](#dicttypehandle).
+
+#### Examples
+
+```yql
+$d = DictTypeComponents(ParseTypeHandle("Dict<Int32,String>"));
+
+SELECT
+ FormatType($d.Key), -- Int32
+ FormatType($d.Payload); -- String
+```
+
+### DictTypeHandle
+
+Building a dictionary-type handle from a key-type handle and a value-type handle passed to arguments. Inverse function: [DictTypeComponents](#dicttypecomponents).
+
+#### Examples
+
+```yql
+SELECT FormatType(
+ DictTypeHandle(
+ ParseTypeHandle("Int32"),
+ ParseTypeHandle("String")
+ )
+); -- Dict<Int32, String>
+```
+
+### ResourceTypeTag
+
+Getting the tag from the resource type handle passed to the argument. Inverse function: [ResourceTypeHandle](#resourcetypehandle).
+
+#### Examples
+
+```yql
+SELECT ResourceTypeTag(ParseTypeHandle("Resource<foo>")); -- foo
+```
+
+### ResourceTypeHandle
+
+Building a resource-type handle from the tag value passed to the argument. Inverse function: [ResourceTypeTag](#resourcetypetag).
+
+#### Examples
+
+```yql
+SELECT FormatType(ResourceTypeHandle("foo")); -- Resource<'foo'>
+```
+
+### TaggedTypeComponents
+
+Getting the tag and the basic type from the decorated type handle passed to the argument. Inverse function: [TaggedTypeHandle](#taggedtypehandle).
+
+#### Examples
+
+```yql
+$t = TaggedTypeComponents(ParseTypeHandle("Tagged<Int32,foo>"));
+
+SELECT FormatType($t.Base), $t.Tag; -- Int32, foo
+```
+
+### TaggedTypeHandle
+
+Constructing a decorated type handle based on the base type handle and the tag name passed in arguments. Inverse function: [TaggedTypeComponents](#taggedtypecomponents).
+
+#### Examples
+
+```yql
+SELECT FormatType(TaggedTypeHandle(
+ ParseTypeHandle("Int32"), "foo"
+)); -- Tagged<Int32, 'foo'>
+```
+
+### VariantTypeHandle
+
+Building a variant-type handle from the handle of the underlying type passed to the argument. Inverse function: [VariantUnderlyingType](#variantunderlyingtype).
+
+#### Examples
+
+```yql
+SELECT FormatType(VariantTypeHandle(
+ ParseTypeHandle("Tuple<Int32, String>")
+)); -- Variant<Int32, String>
+```
+
+### VoidTypeHandle and NullTypeHandle
+
+Constructing a handle for Void and Null types, respectively.
+
+#### Examples
+
+```yql
+SELECT FormatType(VoidTypeHandle()); -- Void
+SELECT FormatType(NullTypeHandle()); -- Null
+```
+
+### CallableTypeComponents
+
+Getting the handle description for the type of callable value passed to the argument. Inverse function: [CallableTypeHandle](#callabletypehandle).
+
+#### Examples
+
+```yql
+$formatArgument = ($x) -> {
+ return AsStruct(
+ FormatType($x.Type) as Type,
+ $x.Name as Name,
+ $x.Flags as Flags
+ )
+};
+
+$formatCallable = ($x) -> {
+ return AsStruct(
+ $x.OptionalArgumentsCount as OptionalArgumentsCount,
+ $x.Payload as Payload,
+ FormatType($x.Result) as Result,
+ ListMap($x.Arguments, $formatArgument) as Arguments
+ )
+};
+
+SELECT $formatCallable(
+ CallableTypeComponents(
+ ParseTypeHandle("(Int32,[bar:Double?{Flags:AutoMap}])->String")
+ )
+); -- (OptionalArgumentsCount: 1, Payload: "", Result: "String", Arguments: [
+ -- (Type: "Int32", Name: "", Flags: []),
+ -- (Type: "Double?", Name: "bar", Flags: ["AutoMap"]),
+ -- ])
+```
+
+### CallableArgument
+
+Packing the description of the argument of the callable value into the structure to be passed to the [CallableTypeHandle](#callabletypehandle) function with the following arguments:
+
+1. Argument type handle.
+2. Optional argument name. The default value is an empty string.
+3. A list of strings with optional argument flags. The default value is an empty list. Supported flags are "AutoMap".
+
+### CallableTypeHandle
+
+Constructing the type handle of the called value using the following arguments:
+
+1. Handle of the return value type.
+2. List of descriptions of arguments received using the [CallableArgument](#callableargument) function.
+3. Optional number of optional arguments in the callable value. The default value is 0.
+4. An optional label for the called value type. The default value is an empty string.
+
+Inverse function: [CallableTypeComponents](#callabletypecomponents).
+
+#### Examples
+
+```yql
+SELECT FormatType(
+ CallableTypeHandle(
+ ParseTypeHandle("String"),
+ AsList(
+ CallableArgument(ParseTypeHandle("Int32")),
+ CallableArgument(ParseTypeHandle("Double?"), "bar", AsList("AutoMap"))
+ ),
+ 1
+ )
+); -- Callable<(Int32,['bar':Double?{Flags:AutoMap}])->String>
+```
+
+### LambdaArgumentsCount
+
+Getting the number of arguments in a lambda function.
+
+#### Examples
+
+```yql
+SELECT LambdaArgumentsCount(($x, $y)->($x+$y))
+; -- 2
+```
diff --git a/yql/essentials/docs/en/builtins/window.md b/yql/essentials/docs/en/builtins/window.md
new file mode 100644
index 00000000000..f9a670167be
--- /dev/null
+++ b/yql/essentials/docs/en/builtins/window.md
@@ -0,0 +1,247 @@
+
+# List of window functions in YQL
+
+The syntax for calling window functions is detailed in a [separate article](../syntax/window.md).
+
+
+
+## Aggregate functions {#aggregate-functions}
+
+All [aggregate functions](aggregation.md) can also be used as window functions.
+In this case, each row includes an aggregation result obtained on a set of rows from the [window frame](../syntax/window.md#frame).
+
+### Examples
+
+```yql
+SELECT
+ SUM(int_column) OVER w1 AS running_total,
+ SUM(int_column) OVER w2 AS total,
+FROM my_table
+WINDOW
+ w1 AS (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),
+ w2 AS ();
+```
+
+
+
+## ROW_NUMBER {#row_number}
+
+Row number within a [partition](../syntax/window.md#partition). No arguments.
+
+### Signature
+
+```yql
+ROW_NUMBER()->Uint64
+```
+
+### Examples
+
+```yql
+SELECT
+ ROW_NUMBER() OVER w AS row_num
+FROM my_table
+WINDOW w AS (ORDER BY key);
+```
+
+
+
+## LAG / LEAD {#lag-lead}
+
+Accessing a value from a row in the [section](../syntax/window.md#partition) that lags behind (`LAG`) or leads (`LEAD`) the current row by a fixed number. The first argument specifies the expression to be accessed, and the second argument specifies the offset in rows. You may omit the offset. By default, the neighbor row is used: the previous or next, respectively (hence, 1 is assumed by default). For the rows having no neighbors at a given distance (for example, `LAG(expr, 3)` `NULL` is returned in the first and second rows of the section).
+
+### Signature
+
+```yql
+LEAD(T[,Int32])->T?
+LAG(T[,Int32])->T?
+```
+
+### Examples
+
+```yql
+SELECT
+ int_value - LAG(int_value) OVER w AS int_value_diff
+FROM my_table
+WINDOW w AS (ORDER BY key);
+```
+
+```yql
+SELECT item, odd, LAG(item, 1) OVER w as lag1 FROM (
+ SELECT item, item % 2 as odd FROM (
+ SELECT AsList(1, 2, 3, 4, 5, 6, 7) as item
+ )
+ FLATTEN BY item
+)
+WINDOW w As (
+ PARTITION BY odd
+ ORDER BY item
+);
+
+/* Output:
+item odd lag1
+--------------------
+2 0 NULL
+4 0 2
+6 0 4
+1 1 NULL
+3 1 1
+5 1 3
+7 1 5
+*/
+```
+
+
+## FIRST_VALUE / LAST_VALUE
+
+Access values from the first and last rows (using the `ORDER BY` clause for the window) of the [window frame](../syntax/window.md#frame). The only argument is the expression that you need to access.
+
+Optionally, `OVER` can be preceded by the additional modifier `IGNORE NULLS`. It changes the behavior of functions to the first or last **non-empty** (i.e., non-`NULL`) value among the window frame rows. The antonym of this modifier is `RESPECT NULLS`: it's the default behavior that can be omitted.
+
+### Signature
+
+```yql
+FIRST_VALUE(T)->T?
+LAST_VALUE(T)->T?
+```
+
+### Examples
+
+```yql
+SELECT
+ FIRST_VALUE(my_column) OVER w
+FROM my_table
+WINDOW w AS (ORDER BY key);
+```
+
+```yql
+SELECT
+ LAST_VALUE(my_column) IGNORE NULLS OVER w
+FROM my_table
+WINDOW w AS (ORDER BY key);
+```
+
+
+
+## NTH_VALUE
+
+Access a value from a row specified by position in the window's `ORDER BY` order within [window frame](../syntax/window.md#frame). Arguments - the expression to access and the row number, starting with 1.
+
+Optionally, the `IGNORE NULLS` modifier can be specified before `OVER`, which causes rows with `NULL` in the first argument's value to be skipped. The antonym of this modifier is `RESPECT NULLS`, which is the default behavior and may be skipped.
+
+### Signature
+
+```yql
+NTH_VALUE(T,N)->T?
+```
+
+### Examples
+
+```yql
+SELECT
+ NTH_VALUE(my_column, 2) OVER w
+FROM my_table
+WINDOW w AS (ORDER BY key);
+```
+
+```yql
+SELECT
+ NTH_VALUE(my_column, 3) IGNORE NULLS OVER w
+FROM my_table
+WINDOW w AS (ORDER BY key);
+```
+
+
+
+
+## RANK / DENSE_RANK / PERCENT_RANK {#rank}
+
+Number the groups of neighboring rows in the [partition](../syntax/window.md#partition) with the same expression value in the argument. `DENSE_RANK` numbers the groups one by one, and `RANK` skips `(N - 1)` values, with `N` being the number of rows in the previous group. `PERCENT_RANK` returns the relative rank of the current row: $(RANK - 1)/(number of rows in the partition - 1)$.
+
+If there is no argument, it uses the order specified in the `ORDER BY` section in the window definition.
+If the argument is omitted and `ORDER BY` is not specified, then all rows are considered equal to each other.
+
+{% note info %}
+
+Passing an argument to `RANK`/`DENSE_RANK`/`PERCENT_RANK` is a non-standard extension in YQL.
+
+{% endnote %}
+
+### Signature
+
+```text
+RANK([T])->Uint64
+DENSE_RANK([T])->Uint64
+PERCENT_RANK([T])->Double
+```
+
+### Examples
+
+```yql
+SELECT
+ RANK(my_column) OVER w
+FROM my_table
+WINDOW w AS (ORDER BY key);
+```
+
+```yql
+SELECT
+ DENSE_RANK() OVER w
+FROM my_table
+WINDOW w AS (ORDER BY my_column);
+```
+
+```yql
+SELECT
+ PERCENT_RANK() OVER w
+FROM my_table
+WINDOW w AS (ORDER BY my_column);
+```
+
+
+
+## NTILE
+
+Distributes the rows of an ordered [partition](../syntax/window.md#partition) into a specified number of groups. The groups are numbered starting with one. For each row, the `NTILE` function returns the number of the group to which the row belongs.
+
+### Signature
+
+```yql
+NTILE(Uint64)->Uint64
+```
+
+### Examples
+
+```yql
+SELECT
+ NTILE(10) OVER w AS group_num
+FROM my_table
+WINDOW w AS (ORDER BY key);
+```
+
+
+
+## CUME_DIST
+
+Returns the relative position (> 0 and <= 1) of a row within a [partition](../syntax/window.md#partition). No arguments.
+
+### Signature
+
+```yql
+CUME_DIST()->Double
+```
+
+### Examples
+
+```yql
+SELECT
+ CUME_DIST() OVER w AS dist
+FROM my_table
+WINDOW w AS (ORDER BY key);
+```
+
+
+## SessionState() {#session-state}
+
+A non-standard window function `SessionState()` (without arguments) lets you get the session calculation status from [SessionWindow](../syntax/group_by.md#session-window) for the current row.
+It's allowed only if `SessionWindow()` is present in the `PARTITION BY` section in the window definition.
+
diff --git a/yql/essentials/docs/en/index.md b/yql/essentials/docs/en/index.md
new file mode 100644
index 00000000000..77431a8793c
--- /dev/null
+++ b/yql/essentials/docs/en/index.md
@@ -0,0 +1,10 @@
+# YQL - Overview
+
+*YQL* (Yandex Query Language) — is a universal declarative query language for distributed systems of data storage and processing, a dialect of SQL.
+
+This documentation section contains the YQL reference that includes the sections:
+
+- [Data types](types/index.md) with a description of data types used in YQL
+- [Syntax](syntax/index.md) with a full list of YQL commands
+- [Built-in functions](builtins/index.md) with a description of the available built-in functions
+- [Recipes](recipes/index.md) with a description of recipes for various tasks
diff --git a/yql/essentials/docs/en/recipes/accessing-json.md b/yql/essentials/docs/en/recipes/accessing-json.md
new file mode 100644
index 00000000000..c18412d95b5
--- /dev/null
+++ b/yql/essentials/docs/en/recipes/accessing-json.md
@@ -0,0 +1,103 @@
+# Accessing values inside JSON with YQL
+
+YQL provides two main ways to retrieve values from JSON:
+
+- Using [**JSON functions from the SQL standard**](../builtins/json.md). This approach is recommended for simple cases and for teams that are familiar with them from other DBMSs.
+- Using [**Yson UDF**](../udf/list/yson.md), [list](../builtins/list.md) and [dict](../builtins/dict.md) builtins, and [lambdas](../syntax/expressions.md#lambda). This approach is more flexible and tightly integrated with YQL's data type system, thus recommended for complex cases.
+
+Below are the recipes that will use the same input JSON to demonstrate how to use each option to check whether a key exists, get a specific value, and retrieve a subtree.
+
+## JSON functions
+
+```yql
+$json = @@{
+ "friends": [
+ {
+ "name": "James Holden",
+ "age": 35
+ },
+ {
+ "name": "Naomi Nagata",
+ "age": 30
+ }
+ ]
+}@@j;
+
+SELECT
+ JSON_EXISTS($json, "$.friends[*].name"), -- True
+ CAST(JSON_VALUE($json, "$.friends[0].age") AS Int32), -- 35
+ JSON_QUERY($json, "$.friends[0]"); -- {"name": "James Holden", "age": 35}
+```
+
+`JSON_*` functions expect the `Json` data type as an input to run. In this example, the string literal has the suffix `j`, marking it as `Json`. In tables, data could be stored in either JSON format or as a string representation. To convert data from `String` to `JSON` data type, use the `CAST` function, such as `CAST(my_string AS JSON)`.
+
+## Yson UDF
+
+This approach typically combines multiple functions and expressions, so a query might leverage different specific strategies.
+
+### Convert the whole JSON to YQL containers
+
+```yql
+$json = @@{
+ "friends": [
+ {
+ "name": "James Holden",
+ "age": 35
+ },
+ {
+ "name": "Naomi Nagata",
+ "age": 30
+ }
+ ]
+}@@j;
+
+$containers = Yson::ConvertTo($json, Struct<friends:List<Struct<name:String?,age:Int32?>>>);
+$has_name = ListAny(
+ ListMap($containers.friends, ($friend) -> {
+ return $friend.name IS NOT NULL;
+ })
+);
+$get_age = $containers.friends[0].age;
+$get_first_friend = Yson::SerializeJson(Yson::From($containers.friends[0]));
+
+SELECT
+ $has_name, -- True
+ $get_age, -- 35
+ $get_first_friend; -- {"name": "James Holden", "age": 35}
+```
+
+It is **not** necessary to convert the whole JSON object to a structured combination of containers. Some fields can be omitted if not used, while some subtrees could be left in an unstructured data type like `Json`.
+
+### Work with in-memory representation
+
+```yql
+$json = @@{
+ "friends": [
+ {
+ "name": "James Holden",
+ "age": 35
+ },
+ {
+ "name": "Naomi Nagata",
+ "age": 30
+ }
+ ]
+}@@j;
+
+$has_name = ListAny(
+ ListMap(Yson::ConvertToList($json.friends), ($friend) -> {
+ return Yson::Contains($friend, "name");
+ })
+);
+$get_age = Yson::ConvertToInt64($json.friends[0].age);
+$get_first_friend = Yson::SerializeJson($json.friends[0]);
+
+SELECT
+ $has_name, -- True
+ $get_age, -- 35
+ $get_first_friend; -- {"name": "James Holden", "age": 35}
+```
+
+## See also
+
+- [{#T}](modifying-json.md)
diff --git a/yql/essentials/docs/en/recipes/index.md b/yql/essentials/docs/en/recipes/index.md
new file mode 100644
index 00000000000..904887e2549
--- /dev/null
+++ b/yql/essentials/docs/en/recipes/index.md
@@ -0,0 +1,9 @@
+# YQL recipes
+
+This section contains query recipes for various tasks that can be solved with YQL.
+
+Table of contents:
+
+* [{#T}](accessing-json.md)
+* [{#T}](modifying-json.md)
+
diff --git a/yql/essentials/docs/en/recipes/modifying-json.md b/yql/essentials/docs/en/recipes/modifying-json.md
new file mode 100644
index 00000000000..3619aa1137e
--- /dev/null
+++ b/yql/essentials/docs/en/recipes/modifying-json.md
@@ -0,0 +1,23 @@
+# Modifying JSON with YQL
+
+In memory, YQL operates on immutable values. Thus, when a query needs to change something inside a JSON value, the mindset should be about constructing a new value from pieces of the old one.
+
+This example query takes an input JSON named `$fields`, parses it, substitutes key `a` with 0, drops key `d`, and adds a key `c` with value 3:
+
+```yql
+$fields = '{"a": 1, "b": 2, "d": 4}'j;
+$pairs = DictItems(Yson::ConvertToInt64Dict($fields));
+$result_pairs = ListExtend(ListNotNull(ListMap($pairs, ($item) -> {
+ $item = if ($item.0 == "a", ("a", 0), $item);
+ return if ($item.0 == "d", null, $item);
+})), [("c", 3)]);
+$result_dict = ToDict($result_pairs);
+SELECT Yson::SerializeJson(Yson::From($result_dict));
+```
+
+## See also
+
+- [{#T}](../udf/list/yson.md)
+- [{#T}](../builtins/list.md)
+- [{#T}](../builtins/dict.md)
+- [{#T}](accessing-json.md)
diff --git a/yql/essentials/docs/en/recipes/toc_i.yaml b/yql/essentials/docs/en/recipes/toc_i.yaml
new file mode 100644
index 00000000000..70ad056eef6
--- /dev/null
+++ b/yql/essentials/docs/en/recipes/toc_i.yaml
@@ -0,0 +1,7 @@
+items:
+- name: Overview
+ href: index.md
+- name: Accessing JSON
+ href: accessing-json.md
+- name: Modifying JSON
+ href: modifying-json.md \ No newline at end of file
diff --git a/yql/essentials/docs/en/syntax/_assets/join-YQL-06.png b/yql/essentials/docs/en/syntax/_assets/join-YQL-06.png
new file mode 100644
index 00000000000..aae84322b7c
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/_assets/join-YQL-06.png
Binary files differ
diff --git a/yql/essentials/docs/en/syntax/action.md b/yql/essentials/docs/en/syntax/action.md
new file mode 100644
index 00000000000..6ec0f0f71f3
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/action.md
@@ -0,0 +1,145 @@
+# ACTION
+
+## DEFINE ACTION {#define-action}
+
+Specifies a named action that is a parameterizable block of multiple top-level expressions.
+
+### Syntax
+
+1. `DEFINE ACTION`: action definition.
+1. [Action name](expressions.md#named-nodes) that will be used to access the defined action further in the query.
+1. The values of parameter names are listed in parentheses.
+1. `AS` keyword.
+1. List of top-level expressions.
+1. `END DEFINE`: The marker of the last expression inside the action.
+
+One or more of the last parameters can be marked with a question mark `?` as optional. If they are omitted during the call, they will be assigned the `NULL` value.
+
+## DO {#do}
+
+Executes an `ACTION` with the specified parameters.
+
+### Syntax
+
+1. `DO`: Executing an action.
+1. The named expression for which the action is defined.
+1. The values to be used as parameters are listed in parentheses.
+
+`EMPTY_ACTION`: An action that does nothing.
+
+{% note info %}
+
+In large queries, you can use separate files for action definition and include them to the main query using [EXPORT](export_import.md#export) + [IMPORT](export_import.md#import) so that instead of one long text you can have several logical parts that are easier to navigate. An important nuance: the `USE my_cluster;` directive in the import query does not affect behavior of actions declared in other files.
+
+{% endnote %}
+
+### Example
+
+```yql
+DEFINE ACTION $hello_world($name, $suffix?) AS
+ $name = $name ?? ($suffix ?? "world");
+ SELECT "Hello, " || $name || "!";
+END DEFINE;
+
+DO EMPTY_ACTION();
+DO $hello_world(NULL);
+DO $hello_world("John");
+DO $hello_world(NULL, "Earth");
+```
+
+## BEGIN .. END DO {#begin}
+
+Performing an action without declaring it (anonymous action).
+
+### Syntax
+
+1. `BEGIN`.
+1. List of top-level expressions.
+1. `END DO`.
+
+An anonymous action can't include any parameters.
+
+### Example
+
+```yql
+DO BEGIN
+ SELECT 1;
+ SELECT 2 -- here and in the previous example, you might omit ';' before END
+END DO
+```
+
+## EVALUATE IF {#evaluate-if}
+
+`EVALUATE IF`: Executing an action depending on the condition. It's followed by:
+
+1. Condition.
+2. [DO](#do) with the name and parameters of the action or an anonymous action.
+3. An optional `ELSE` followed by the second `DO` for a situation where the condition is not met.
+
+## EVALUATE FOR {#evaluate-for}
+
+`EVALUATE FOR`: Executing an action for each item in the list. It's followed by:
+
+1. [A named expression](expressions.md#named-nodes) applied to each next element in the list.
+2. `IN` keyword.
+3. The above-declared named expression applied to the list the action is executed on.
+4. [DO](#do) with the name and parameters of an action or an anonymous action. In the parameters, you can use both the current element from the first paragraph and any named expressions declared above, including the list itself.
+5. An optional `ELSE` followed by the second `DO` for the situation when the list is empty.
+
+### Examples
+
+```yql
+DEFINE ACTION $hello() AS
+ SELECT "Hello!";
+END DEFINE;
+
+DEFINE ACTION $bye() AS
+ SELECT "Bye!";
+END DEFINE;
+
+EVALUATE IF RANDOM(0) > 0.5
+ DO $hello()
+ELSE
+ DO $bye();
+
+EVALUATE IF RANDOM(0) > 0.1 DO BEGIN
+ SELECT "Hello!";
+END DO;
+
+EVALUATE FOR $i IN AsList(1, 2, 3) DO BEGIN
+ SELECT $i;
+END DO;
+```
+
+```yql
+-- copy the $input table to $count of new tables
+$count = 3;
+$input = "my_input";
+$inputs = ListReplicate($input, $count);
+$outputs = ListMap(
+ ListFromRange(0, $count),
+ ($i) -> {
+ RETURN "tmp/out_" || CAST($i as String)
+ }
+);
+$pairs = ListZip($inputs, $outputs);
+
+DEFINE ACTION $copy_table($pair) as
+ $input = $pair.0;
+ $output = $pair.1;
+ INSERT INTO $output WITH TRUNCATE
+ SELECT * FROM $input;
+END DEFINE;
+
+EVALUATE FOR $pair IN $pairs
+ DO $copy_table($pair)
+ELSE
+ DO EMPTY_ACTION (); -- you may omit this ELSE,
+ -- do nothing is implied by default
+```
+
+{% note info %}
+
+Note that `EVALUATE` is run before the operation starts.
+
+{% endnote %}
diff --git a/yql/essentials/docs/en/syntax/commit.md b/yql/essentials/docs/en/syntax/commit.md
new file mode 100644
index 00000000000..eace340a5c8
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/commit.md
@@ -0,0 +1,17 @@
+# COMMIT
+
+By default, the entire YQL query is executed within a single transaction, and independent parts inside it are executed in parallel, if possible.
+Using the `COMMIT;` keyword you can add a barrier to the execution process to delay execution of expressions that follow until all the preceding expressions have completed.
+
+To commit in the same way automatically after each expression in the query, you can use `PRAGMA autocommit;`.
+
+## Examples
+
+```yql
+INSERT INTO result1 SELECT * FROM my_table;
+INSERT INTO result2 SELECT * FROM my_table;
+COMMIT;
+-- result2 will already include the SELECT contents from the second line:
+INSERT INTO result3 SELECT * FROM result2;
+```
+
diff --git a/yql/essentials/docs/en/syntax/declare.md b/yql/essentials/docs/en/syntax/declare.md
new file mode 100644
index 00000000000..93cd978cd41
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/declare.md
@@ -0,0 +1,36 @@
+# DECLARE
+
+Declares a typed [named expression](expressions.md#named-nodes) whose value will be passed separately from the query text. With parameterization, you can separately develop an analytical solution and then launch it sequentially with different input values.
+
+In the case of transactional load, parameters let you avoid recompilation of queries when repeating calls of the same type. This way you can reduce server utilization and exclude compilation time from the total time of query execution.
+
+Passing of parameters is supported in the SDK, CLI, and graphical interfaces.
+
+## Syntax
+
+```yql
+DECLARE $named-node AS data_type;
+```
+
+1. `DECLARE` keyword.
+1. `$named-node`: The name by which you can access the passed value. It must start with `$`.
+1. `AS` keyword.
+1. `data_type` is the data type [represented as a string in the accepted format](../types/type_string.md).
+
+Only serializable data types are allowed:
+
+* [Primitive types](../types/primitive.md).
+* [Optional types](../types/optional.md).
+* [Containers](../types/containers.md), except `Stream<Type>`.
+* `Void` and `Null` are the supported [special types](../types/special.md).
+
+## Example
+
+```yql
+DECLARE $x AS String;
+DECLARE $y AS String?;
+DECLARE $z AS List<String>;
+
+SELECT $x, $y, $z;
+```
+
diff --git a/yql/essentials/docs/en/syntax/discard.md b/yql/essentials/docs/en/syntax/discard.md
new file mode 100644
index 00000000000..83364abd8bb
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/discard.md
@@ -0,0 +1,29 @@
+# DISCARD
+
+Calculates [`SELECT`](select/index.md), [`REDUCE`](reduce.md), or [`PROCESS`](process.md) without returning the result neither to the client or table. You can't use it along with [INTO RESULT](into_result.md).
+
+It's good to combine it with [`Ensure`](../builtins/basic.md#ensure) to check the final calculation result against the user's criteria.
+
+
+## Examples
+
+```yql
+DISCARD SELECT 1;
+```
+
+```yql
+INSERT INTO result_table WITH TRUNCATE
+SELECT * FROM
+my_table
+WHERE value % 2 == 0;
+
+COMMIT;
+
+DISCARD SELECT Ensure(
+ 0, -- will discard result anyway
+ COUNT(*) > 1000,
+ "Too small result table, got only " || CAST(COUNT(*) AS String) || " rows"
+) FROM result_table;
+```
+
+
diff --git a/yql/essentials/docs/en/syntax/export_import.md b/yql/essentials/docs/en/syntax/export_import.md
new file mode 100644
index 00000000000..ddebe256bee
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/export_import.md
@@ -0,0 +1,63 @@
+# Putting part of the query into a separate file
+
+Here's the mechanism for putting part of the query into a separate attached file:
+
+* [PRAGMA Library](pragma.md#library) marks the attached file as available for import.
+
+## `Export`
+
+* `EXPORT $my_symbol1, $my_symbol2, ...;` lists the names of named expressions in the library that are available for import.
+
+## `Import`
+
+* `IMPORT my_library SYMBOLS $my_symbol1, $my_symbol2, ...;` makes the listed named expressions available for further use.
+
+{% note info %}
+
+You can use the library to include [lambdas](expressions.md#lambda), [actions](action.md), [named subqueries](subquery.md), constants and expressions, but **not subqueries or aggregate functions**.
+
+{% endnote %}
+
+{% note warning %}
+
+The file linked by the [PRAGMA Library](pragma.md#library) must be attached to the query. **You can't use a [PRAGMA File](pragma.md#file) for this purpose**.
+
+{% endnote %}
+
+### Examples
+
+my_lib.sql:
+
+```yql
+$Square = ($x) -> { RETURN $x * $x; };
+$Sqrt = ($x) -> { RETURN Math::Sqrt($x); };
+
+-- Aggregate functions created by
+-- AggregationFactory, it makes sense to add it to the library
+$Agg_sum = AggregationFactory("SUM");
+$Agg_max = AggregationFactory("MAX");
+
+EXPORT $Square, $Sqrt, $Agg_sum, $Agg_max;
+```
+
+Query:
+
+```yql
+PRAGMA Library("my_lib.sql");
+IMPORT my_lib SYMBOLS $Square, $Sqrt, $Agg_sum, $Agg_max;
+SELECT
+ $Square(2), -- 4
+ $Sqrt(4); -- 2
+
+SELECT
+ AGGREGATE_BY(x, $Agg_sum), -- 5
+ AGGREGATE_BY(x, $Agg_max) -- 3
+FROM (
+ SELECT 2 AS x
+ UNION ALL
+ SELECT 3 AS x
+)
+```
+
+
+
diff --git a/yql/essentials/docs/en/syntax/expressions.md b/yql/essentials/docs/en/syntax/expressions.md
new file mode 100644
index 00000000000..02ba8372dde
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/expressions.md
@@ -0,0 +1,550 @@
+# Expressions
+
+<!-- markdownlint-disable blanks-around-fences -->
+
+## String concatenation {#concatenation}
+
+Executed using the binary operator `||`.
+
+As with other binary operators, if the data on either side is `NULL`, the result is also `NULL`.
+
+Don't confuse this operator with a logical "or": in SQL, it's denoted by the `OR` keyword. It's also not worth doing concatenation using `+`.
+
+### Examples
+
+```yql
+SELECT "fo" || "o";
+```
+
+
+
+## Matching a string by pattern {#check-match}
+
+`REGEXP` and `RLIKE` are aliases used to call [Re2::Grep](../udf/list/re2.md#match). `MATCH`: Same for [Re2::Match](../udf/list/re2.md#match).
+
+`LIKE` works as follows:
+
+* Patterns can include two special characters:
+
+ * `%`: Zero or more of any characters.
+ * `_`: Exactly one of any character.
+
+All other characters are literals that represent themselves.
+
+* As opposed to `REGEXP`, `LIKE` must be matched exactly. For example, to search a substring, add `%` at the beginning and end of the pattern.
+* `ILIKE` is a case-insensitive version of `LIKE`.
+* If `LIKE` is applied to the key column of the sorted table and the pattern doesn't start with a special character, filtering by prefix drills down directly to the cluster level, which in some cases lets you avoid the full table scan. This optimization is disabled for `ILIKE`.
+* To escape special characters, specify the escaped character after the pattern using the `ESCAPE '?'` keyword. Instead of `?` you can use any character except `%`, `_` and `\`. For example, if you use a question mark as an escape character, the expressions `?%`, `?_` and `??` will match their second character in the template: percent, underscore, and question mark, respectively. The escape character is undefined by default.
+
+The most popular way to use the `LIKE` and `REGEXP` keywords is to filter a table using the statements with the `WHERE` clause. However, there are no restrictions on using templates in this context: you can use them in most of contexts involving strings, for example, with concatenation by using `||`.
+
+### Examples
+
+```yql
+SELECT * FROM my_table
+WHERE string_column REGEXP '\\d+';
+-- the second slash is required because
+-- all the standard string literals in SQL
+-- can accept C-escaped strings
+```
+
+```yql
+SELECT
+ string_column LIKE '___!_!_!_!!!!!!' ESCAPE '!'
+ -- searches for a string of exactly 9 characters:
+ -- 3 arbitrary characters
+ -- followed by 3 underscores
+ -- and 3 exclamation marks
+FROM my_table;
+```
+
+```yql
+SELECT * FROM my_table
+WHERE key LIKE 'foo%bar';
+-- if the table is sorted by key, it will only scan the keys,
+-- starting with "foo", and then, among them,
+-- will leave only those that end in "bar"
+```
+
+
+
+## Operators
+
+### Arithmetic operators {#math-operators}
+
+The operators `+`, `-`, `*`, `/`, `%` are defined for [primitive data types](../types/primitive.md) that are variations of numbers.
+
+For the Decimal data type, bankers rounding is used (to the nearest even integer).
+
+#### Examples
+
+```yql
+SELECT 2 + 2;
+```
+
+```yql
+SELECT 0.0 / 0.0;
+```
+
+### Comparison operators {#comparison-operators}
+
+The operators `=`, `==`, `!=`, `<>`, `>`, `<` are defined for:
+
+* Primitive data types except Yson and Json.
+* Tuples and structures with the same set of fields. No order is defined for structures, but you can check for (non-)equality. Tuples are compared element-by-element left to right.
+
+#### Examples
+
+```yql
+SELECT 2 > 1;
+```
+
+### Logical operators {#logic-operators}
+
+Use the operators `AND`, `OR`, `XOR` for logical operations on Boolean values (`Bool`).
+
+#### Examples*
+
+```yql
+SELECT 3 > 0 AND false;
+```
+
+### Bitwise operators {#bit-operators}
+
+Bitwise operations on numbers:
+
+* `&`, `|`, `^`: AND, OR, and XOR, respectively. Don't confuse bitwise operations with the related keywords. The keywords `AND`, `OR`, and `XOR` are used *for Boolean values only*, but not for numbers.
+* ` ~ `: A negation.
+* `<`, `>`: Left or right shifts.
+* `|<`, `>|`: Circular left or right shifts.
+
+#### Examples
+
+```yql
+SELECT
+ key << 10 AS key,
+ ~value AS value
+FROM my_table;
+```
+
+### Precedence and associativity of operators {#operator-priority}
+
+Operator precedence determines the order of evaluation of an expression that contains different operators.
+For example, the expression `1 + 2 * 3` is evaluated as `1 + (2 * 3)` because the multiplication operator has a higher precedence than the addition operator.
+
+Associativity determines the order of evaluating expressions containing operators of the same type.
+For example, the expression `1 + 2 + 3` is evaluated as `(1 + 2) + 3` because the addition operator is left-associative.
+On the other hand, the expression `a ?? b ?? c` is evaluated as `a ?? (b ?? c)` because the `??` operator is right-associative
+
+The table below shows precedence and associativity of YQL operators.
+The operators in the table are listed in descending order of precedence.
+
+| Priority | Operator | Description | Associativity |
+| --- | --- | --- | --- |
+| 1 | `a[], a.foo, a()` | Accessing a container item, calling a function | Left |
+| 2 | `+a, -a, ~a, NOT a` | Unary operators: plus, minus, bitwise and logical negation | Right |
+| 3 | `a\|\|b` | [String concatenation](#concatenation) | Left |
+| 4 | `a*b, a/b, a%b` | Multiplication, division, remainder of division | Left |
+| 5 | `a+b, a-b` | Addition/Subtraction | Left |
+| 6 | `a ?? b` | Operator notation for [NVL/COALESCE](../builtins/basic.md#coalesce) | Right |
+| 7 | `a<b, a>b, a\|<b, a>\|b,` `a\|b, a^b, a&b` | Shift operators and logical bit operators | Left |
+| 8 | `a<b, a=b, a=b, a>b` | Comparison | Left |
+| 9 | `a IN b` | Occurrence of an element in a set | Left |
+| 9 | `a==b, a=b, a!=b, a<>b,` `a is (not) distinct from b` | Comparison for (non-)equality | Left |
+| 10 | `a XOR b` | Logical XOR | Left |
+| 11 | `a AND b` | Logical AND | Left |
+| 12 | `a OR b` | Logical OR | Left |
+
+
+
+## IS \[NOT\] NULL {#is-null}
+
+Matching an empty value (`NULL`). Since `NULL` is a special value [equal to nothing](../types/optional.md#null_expr), the ordinary [comparison operators](#comparison-operators) can't be used to match it.
+
+### Examples
+
+```yql
+SELECT key FROM my_table
+WHERE value IS NOT NULL;
+```
+
+
+
+## IS \[NOT\] DISTINCT FROM {#is-distinct-from}
+
+Comparing of two values. Unlike the regular [comparison operators](#comparison-operators), NULLs are treated as equal to each other.
+More precisely, the comparison is carried out according to the following rules:
+
+1. The operators `IS DISTINCT FROM`/`IS NOT DISTINCT FROM` are defined for those and only for those arguments for which the operators `!=` and `=` are defined.
+2. The result of `IS NOT DISTINCT FROM` is equal to the logical negation of the `IS DISTINCT FROM` result for these arguments.
+3. If the result of the `==` operator is not equal to zero for some arguments, then it is equal to the result of the `IS NOT DISTINCT FROM` operator for the same arguments.
+4. If both arguments are empty `Optional` or `NULL`s, then the value of `IS NOT DISTINCT FROM` is `True`.
+5. The result of `IS NOT DISTINCT FROM` for an empty `Optional` or `NULL` and filled-in `Optional` or non-`Optional` value is `False`.
+
+For values of composite types, these rules are used recursively.
+
+
+## BETWEEN {#between}
+
+Checking whether a value is in a range. It's equivalent to two conditions with `>=` and `<=` (range boundaries are included). Can be used with the `NOT` prefix to support inversion.
+
+### Examples
+
+```yql
+SELECT * FROM my_table
+WHERE key BETWEEN 10 AND 20;
+```
+
+
+
+## IN {#in}
+
+Checking whether a value is inside of a set of values. It's logically equivalent to a chain of equality comparisons using `OR` but implemented more efficiently.
+
+{% note warning "Warning" %}
+
+Unlike a similar keyword in Python, in YQL `IN` **DOES NOT** search for a substring inside a string. To search for a substring, use the function [String::Contains](../udf/list/string.md) or [LIKE/REGEXP](#check-match) mentioned above.
+
+{% endnote %}
+
+Immediately after `IN`, you can specify the `COMPACT` modifier.
+If `COMPACT` is not specified, then `IN` with a subquery is executed as a relevant `JOIN` (`LEFT SEMI` for `IN` and `LEFT ONLY` for `NOT IN`), if possible.
+Using the `COMPACT` modifier forces the in-memory execution strategy: a hash table is immediately built from the contents of the right `IN` part in-memory, and then the left part is filtered.
+
+The `COMPACT` modifier must be used with care. Since the hash table is built in-memory, the query may fail if the right part of `IN` contains many large or different elements.
+
+It is prefirable to add large lists of values to your query by URLs and use the [ParseFile](../builtins/basic.md#parsefile) function.
+
+### Examples
+
+```yql
+SELECT column IN (1, 2, 3)
+FROM my_table;
+```
+
+```yql
+SELECT * FROM my_table
+WHERE string_column IN ("a", "b", "c");
+```
+
+```yql
+$foo = AsList(1, 2, 3);
+SELECT 1 IN $foo;
+```
+
+```yql
+$values = (SELECT column + 1 FROM table);
+SELECT * FROM my_table WHERE
+ -- filtering by an in-memory hash table for one_table
+ column1 IN COMPACT $values AND
+ -- followed by LEFT ONLY JOIN with other_table
+ column2 NOT IN (SELECT other_column FROM other_table);
+```
+
+
+
+## AS {#as}
+
+Can be used in the following scenarios:
+
+* Adding a short name (alias) for columns or tables within the query.
+* Using named arguments in function calls.
+* To specify the target type in the case of explicit type casting, see [CAST](#cast).
+
+### Examples
+
+```yql
+SELECT key AS k FROM my_table;
+```
+
+```yql
+SELECT t.key FROM my_table AS t;
+```
+
+```yql
+SELECT
+ MyFunction(key, 123 AS my_optional_arg)
+FROM my_table;
+```
+
+## CAST {#cast}
+
+<!-- markdownlint-disable blanks-around-fences -->
+
+Tries to cast the value to the specified type. The attempt may fail and return `NULL`. When used with numbers, it may lose precision or most significant bits.
+For lists and dictionaries, it can either delete or replace with `NULL` the elements whose conversion failed.
+For structures and tuples, it deletes elements that are omitted in the target type.
+For more information about casting rules, see [here](../types/cast.md).
+
+{% include [decimal_args](../_includes/decimal_args.md) %}
+
+### Examples
+
+{% include [cast_examples](../_includes/cast_examples.md) %}
+
+
+## BITCAST {#bitcast}
+
+Performs a bitwise conversion of an integer value to the specified integer type. The conversion is always successful, but may lose precision or high-order bits.
+
+### Examples
+
+```yql
+SELECT
+ BITCAST(100000ul AS Uint32), -- 100000
+ BITCAST(100000ul AS Int16), -- -31072
+ BITCAST(100000ul AS Uint16), -- 34464
+ BITCAST(-1 AS Int16), -- -1
+ BITCAST(-1 AS Uint16); -- 65535
+```
+
+## CASE {#case}
+
+Conditional expressions and branching. It's similar to `if`, `switch` and ternary operators in the imperative programming languages.
+If the result of the `WHEN` expression is `true`, the value of the `CASE` expression becomes the result following the condition, and the rest of the `CASE` expression isn't calculated. If the condition is not met, all the `WHEN` clauses that follow are checked. If none of the `WHEN` clauses are met, the `CASE` value is assigned the result from the `ELSE` clause.
+The `ELSE` branch is mandatory in the `CASE` expression. Expressions in `WHEN` are checked sequentially, from top to bottom.
+
+Since its syntax is quite sophisticated, it's often more convenient to use the built-in function [IF](../builtins/basic.md#if).
+
+### Examples
+
+```yql
+SELECT
+ CASE
+ WHEN value > 0
+ THEN "positive"
+ ELSE "negative"
+ END
+FROM my_table;
+```
+
+```yql
+SELECT
+ CASE value
+ WHEN 0 THEN "zero"
+ WHEN 1 THEN "one"
+ ELSE "not zero or one"
+ END
+FROM my_table;
+```
+
+
+
+## Named expressions {#named-nodes}
+
+Complex queries may be sophisticated, containing lots of nested levels and/or repeating parts. In YQL, you can use named expressions to assign a name to an arbitrary expression or subquery. Named expressions can be referenced in other expressions or subqueries. In this case, the original expression/subquery is actually substituted at point of use.
+
+A named expression is defined as follows:
+
+```yql
+<named-expr> = <expression> | <subquery>;
+```
+
+Here `<named-expr>` consists of a $ character and an arbitrary non-empty identifier (for example, `$foo`).
+
+If the expression on the right is a tuple, you can automatically unpack it by specifying several named expressions separated by commas on the left:
+
+```yql
+<named-expr1>, <named-expr2>, <named-expr3> ... = <expression-returning-tuple>;
+```
+
+In this case, the number of expressions must match the tuple size.
+
+Each named expression has a scope. It starts immediately after the definition of a named expression and ends at the end of the nearest enclosed namescope (for example, at the end of the query or at the end of the body of the [lambda function](#lambda), [ACTION](action.md#define-action), [SUBQUERY](subquery.md#define-subquery), or the cycle [EVALUATE FOR](action.md#evaluate-for)).
+Redefining a named expression with the same name hides the previous expression from the current scope.
+
+If the named expression has never been used, a warning is issued. To avoid such a warning, use the underscore as the first character in the ID (for example, `$_foo`).
+The named expression `$_` is called an anonymous named expression and is processed in a special way: it works as if `$_` would be automatically replaced by `$_<some_uniq_name>`.
+Anonymous named expressions are convenient when you don't need the expression value. For example, to fetch the second element from a tuple of three elements, you can write:
+
+```yql
+$_, $second, $_ = AsTuple(1, 2, 3);
+select $second;
+```
+
+An attempt to reference an anonymous named expression results in an error:
+
+```yql
+$_ = 1;
+select $_; --- error: Unable to reference anonymous name $_
+export $_; --- An error: Can not export anonymous name $_
+```
+
+Moreover, you can't import a named expression with an anonymous alias:
+
+```yql
+import utils symbols $sqrt as $_; --- error: Can not import anonymous name $_
+```
+
+Anonymous argument names are also supported for [lambda functions](#lambda), [ACTION](action.md#define-action), [SUBQUERY](subquery.md#define-subquery), and in [EVALUATE FOR](action.md#evaluate-for).
+
+{% note info %}
+
+If named expression substitution results in completely identical subgraphs in the query execution graph, the graphs are combined to execute a subgraph only once.
+
+{% endnote %}
+
+### Examples
+
+```yql
+$multiplier = 712;
+SELECT
+ a * $multiplier, -- $multiplier is 712
+ b * $multiplier,
+ (a + b) * $multiplier
+FROM abc_table;
+$multiplier = c;
+SELECT
+ a * $multiplier -- $multiplier is column c
+FROM abc_table;
+```
+
+```yql
+$intermediate = (
+ SELECT
+ value * value AS square,
+ value
+ FROM my_table
+);
+SELECT a.square * b.value
+FROM $intermediate AS a
+INNER JOIN $intermediate AS b
+ON a.value == b.square;
+```
+
+```yql
+$a, $_, $c = AsTuple(1, 5u, "test"); -- unpack a tuple
+SELECT $a, $c;
+```
+
+```yql
+$x, $y = AsTuple($y, $x); -- swap expression values
+```
+
+## Table expressions {#table-contexts}
+
+A table expression is an expression that returns a table. Table expressions in YQL are as follows:
+
+* Subqueries: `(SELECT key, subkey FROM T)`
+* [Named subqueries](#named-nodes): `$foo = SELECT * FROM T;` (in this case, `$foo` is also a table expression)
+* [Subquery templates](subquery.md#define-subquery): `DEFINE SUBQUERY $foo($name) AS ... END DEFINE;` (`$foo("InputTable")` is a table expression).
+
+Semantics of a table expression depends on the context where it is used. In YQL, table expressions can be used in the following contexts:
+
+* Table context: after [FROM](select/from.md). In this case, table expressions work as expected: for example, `$input = SELECT a, b, c FROM T; SELECT * FROM $input` returns a table with three columns. The table context also occurs after [UNION ALL](select/union.md#unionall), [JOIN](join.md#join), [PROCESS](process.md#process), [REDUCE](reduce.md#reduce);
+* Vector context: after [IN](#in). In this context, the table expression must contain exactly one column (the name of this column doesn't affect the expression result in any way). A table expression in a vector context is typed as a list (the type of the list element is the same as the column type in this case). Example: `SELECT * FROM T WHERE key IN (SELECT k FROM T1)`;
+* A scalar context arises *in all the other cases*. As in a vector context, a table expression must contain exactly one column, but the value of the table expression is a scalar, that is, an arbitrarily selected value of this column (if no rows are returned, the result is `NULL`). Example: `$count = SELECT COUNT(*) FROM T; SELECT * FROM T ORDER BY key LIMIT $count / 2`;
+
+The order of rows in a table context, the order of elements in a vector context, and the rule for selecting a value from a scalar context (if multiple values are returned), aren't defined. This order also cannot be affected by `ORDER BY`: `ORDER BY` without `LIMIT` is ignored in table expressions with a warning, and `ORDER BY` with `LIMIT` defines a set of elements rather than the order within that set.
+
+There is an exception to this rule. Named expression with [PROCESS](process.md#process), if used in a scalar context, behaves as in a table context:
+
+```yql
+$input = SELECT 1 AS key, 2 AS value;
+$process = PROCESS $input;
+
+SELECT FormatType(TypeOf($process)); -- $process is used in a scalar context,
+ -- but the SELECT result in this case is List<Struct'key':Int32,'value':Int32>
+
+SELECT $process[0].key; -- that returns 1
+
+SELECT FormatType(TypeOf($input)); -- throws an error: $input in a scalar context must contain one column
+```
+
+{% note warning %}
+
+A common error is to use an expression in a scalar context rather than a table context or vector context. For example:
+
+```yql
+$dict = SELECT key, value FROM T1;
+
+DEFINE SUBQUERY $merge_dict($table, $dict) AS
+SELECT * FROM $table LEFT JOIN $dict USING(key);
+END DEFINE;
+
+SELECT * FROM $merge_dict("Input", $dict); -- $dict is used in a scalar context in this case.
+ -- an error: exactly one column is expected in a scalar context
+```
+
+A correct notation in this case is:
+
+```yql
+DEFINE SUBQUERY $dict() AS
+SELECT key, value FROM T1;
+END DEFINE;
+
+DEFINE SUBQUERY $merge_dict($table, $dict) AS
+SELECT * FROM $table LEFT JOIN $dict() USING(key); -- Using the table expression $dict()
+ -- (Calling a subquery template) in a table context
+END DEFINE;
+
+SELECT * FROM $merge_dict("Input", $dict); -- $dict - is a subquery template (rather than a table expression)
+ -- that is passed as an argument of a table expression
+```
+
+{% endnote %}
+
+## Lambda functions {#lambda}
+
+Let you combine multiple expressions into a single callable value.
+
+List arguments in round brackets, following them by the arrow and lambda function body. The lambda function body includes either an expression in round brackets or curly brackets around an optional chain of [named expressions](#named-nodes) assignments and the call result after the `RETURN` keyword in the last expression.
+
+The scope for the lambda body: first the local named expressions, then arguments, then named expressions defined above by the lambda function at the top level of the query.
+
+Only use pure expressions inside the lambda body (those might also be other lambdas, possibly passed through arguments). However, you can't use [SELECT](select/index.md), [INSERT INTO](insert_into.md), or other top-level expressions.
+
+One or more of the last lambda parameters can be marked with a question mark as optional: if they haven't been specified when calling lambda, they are assigned the `NULL` value.
+
+### Examples
+
+```yql
+$f = ($y) -> {
+ $prefix = "x";
+ RETURN $prefix || $y;
+};
+
+$g = ($y) -> ("x" || $y);
+
+$h = ($x, $y?) -> ($x + ($y ?? 0));
+
+SELECT $f("y"), $g("z"), $h(1), $h(2, 3); -- "xy", "xz", 1, 5
+```
+
+```yql
+-- if the lambda result is calculated by a single expression, then you can use a more compact syntax:
+$f = ($x, $_) -> ($x || "suffix"); -- the second argument is not used
+SELECT $f("prefix_", "whatever");
+```
+
+
+
+## Accessing containers {#items-access}
+
+For accessing the values inside containers:
+
+* `Struct<>`, `Tuple<>` and `Variant<>`, use a **dot**. The set of keys (for the tuple and the corresponding variant — indexes) is known at the query compilation time. The key is **validated** before beginning the query execution.
+* `List<>` and `Dict<>`, use **square brackets**. The set of keys (set of indexes for keys) is known only at the query execution time. The key is **not validated** before beginning the query execution. If no value is found, an empty value (NULL) is returned.
+
+[Description and list of available containers](../types/containers.md).
+
+When using this syntax to access containers within table columns, be sure to specify the full column name, including the table name or table alias separated by a dot (see the first example below).
+
+### Examples
+
+```yql
+SELECT
+ t.struct.member,
+ t.tuple.7,
+ t.dict["key"],
+ t.list[7]
+FROM my_table AS t;
+```
+
+```yql
+SELECT
+ Sample::ReturnsStruct().member;
+```
+
+
+
diff --git a/yql/essentials/docs/en/syntax/flatten.md b/yql/essentials/docs/en/syntax/flatten.md
new file mode 100644
index 00000000000..b99f59ef49d
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/flatten.md
@@ -0,0 +1,145 @@
+# FLATTEN
+
+## FLATTEN BY {#flatten-by}
+
+Converts rows in the source table using vertical unpacking of [containers](../types/containers.md) of variable length (lists or dictionaries).
+
+For example:
+
+* Source table:
+
+ |[a, b, c]|1|
+ | --- | --- |
+ |[d]|2|
+ |[]|3|
+
+* The table resulting from `FLATTEN BY` on the left column:
+
+ |a|1|
+ | --- | --- |
+ |b|1|
+ |c|1|
+ |d|2|
+
+
+### Example
+
+```yql
+$sample = AsList(
+ AsStruct(AsList('a','b','c') AS value, CAST(1 AS Uint32) AS id),
+ AsStruct(AsList('d') AS value, CAST(2 AS Uint32) AS id),
+ AsStruct(AsList() AS value, CAST(3 AS Uint32) AS id)
+);
+
+SELECT value, id FROM as_table($sample) FLATTEN BY (value);
+```
+
+This conversion can be convenient in the following cases:
+
+* When it is necessary to output statistics by cells from a container column (for example, via [`GROUP BY`](group_by.md)).
+
+* When the cells in a container column store IDs from another table that you want to join with [`JOIN`](join.md).
+
+### Syntax
+
+* `FLATTEN BY` is specified after `FROM`, but before `GROUP BY`, if `GROUP BY` is present in the query.
+* The type of the result column depends on the type of the source column:
+
+| Container type | Result type | Comments |
+| --- | --- | --- |
+| `List<X>` | `X` | List cell type |
+| `Dict<X,Y>` | `Tuple<X,Y>` | Tuple of two elements containing key-value pairs |
+| `Optional<X>` | `X` | The result is almost equivalent to the clause `WHERE foo IS NOT NULL`, but the `foo` column type is changed to `X` |
+
+* By default, the result column replaces the source column. Use `FLATTEN BY foo AS bar` to keep the source container. As a result, the source container is still available as `foo` and the output container is available as `bar`.
+* To build a Cartesian product of multiple container columns, use the clause `FLATTEN BY (a, b, c)`. Parentheses are mandatory to avoid grammar conflicts.
+* Inside `FLATTEN BY`, you can only use column names from the input table. To apply `FLATTEN BY` to the calculation result, use a subquery.
+* In `FLATTEN BY` you can use both columns and arbitrary named expressions (unlike columns, `AS` is required in this case). To avoid grammatical ambiguities of the expression after `FLATTEN BY`, make sure to use parentheses with the following: `... FLATTEN BY (ListSkip(col, 1) AS col) ...`
+* If the source column had nested containers, for example, `List<DictX,Y>`, `FLATTEN BY` unpacks only the outer level. To completely unpack the nested containers, use a subquery.
+
+{% note info %}
+
+`FLATTEN BY` interprets [optional data types](../types/optional.md) as lists of length 0 or 1. The table rows with `NULL` are skipped, and the column type changes to a similar non-optional type.
+
+`FLATTEN BY` makes only one conversion at a time, so use `FLATTEN LIST BY` or `FLATTEN OPTIONAL BY` on optional containers, for example, `Optional<List<String>>`.
+
+{% endnote %}
+
+
+
+### Specifying the container type {#flatten-by-specific-type}
+
+To specify the type of container to convert to, you can use:
+
+* `FLATTEN LIST BY`
+
+ For `Optional<List<T>>`, `FLATTEN LIST BY` will unpack the list, treating `NULL` as an empty list.
+
+* `FLATTEN DICT BY`
+
+ For `Optional<Dict<T>>`, `FLATTEN DICT BY` will unpack the dictionary, interpreting `NULL` as an empty dictionary.
+
+* `FLATTEN OPTIONAL BY`
+
+ To filter the `NULL` values without serialization, specify the operation by using `FLATTEN OPTIONAL BY`.
+
+### Examples
+
+```yql
+SELECT
+ t.item.0 AS key,
+ t.item.1 AS value,
+ t.dict_column AS original_dict,
+ t.other_column AS other
+FROM my_table AS t
+FLATTEN DICT BY dict_column AS item;
+```
+
+```yql
+SELECT * FROM (
+ SELECT
+ AsList(1, 2, 3) AS a,
+ AsList("x", "y", "z") AS b
+) FLATTEN LIST BY (a, b);
+```
+
+```yql
+SELECT * FROM (
+ SELECT
+ "1;2;3" AS a,
+ AsList("x", "y", "z") AS b
+) FLATTEN LIST BY (String::SplitToList(a, ";") as a, b);
+```
+
+
+
+### Analogues of FLATTEN BY in other DBMS {#flatten-other-dmb}
+
+* PostgreSQL: `unnest`
+* Hive: `LATERAL VIEW`
+* MongoDB: `unwind`
+* Google BigQuery: `FLATTEN`
+* ClickHouse: `ARRAY JOIN / arrayJoin`
+
+
+
+## FLATTEN COLUMNS {#flatten-columns}
+
+Converts a table where all columns must be structures to a table with columns corresponding to each element of each structure from the source columns.
+
+The names of the source column structures are not used and not returned in the result. Be sure that the structure element names aren't repeated in the source columns.
+
+### Example
+
+```yql
+SELECT x, y, z
+FROM (
+ SELECT
+ AsStruct(
+ 1 AS x,
+ "foo" AS y),
+ AsStruct(
+ false AS z)
+) FLATTEN COLUMNS;
+```
+
diff --git a/yql/essentials/docs/en/syntax/group_by.md b/yql/essentials/docs/en/syntax/group_by.md
new file mode 100644
index 00000000000..5740cc63808
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/group_by.md
@@ -0,0 +1,267 @@
+## GROUP BY
+
+Group the `SELECT` results by the values of the specified columns or expressions. `GROUP BY` is often combined with [aggregate functions](../builtins/aggregation.md) (`COUNT`, `MAX`, `MIN`, `SUM`, `AVG`) to perform calculations in each group.
+
+### Syntax
+
+```yql
+SELECT -- In SELECT, you can use:
+ column1, -- key columns specified in GROUP BY
+ key_n, -- named expressions specified in GROUP BY
+ column1 + key_n, -- arbitrary non-aggregate functions on them
+ Aggr_Func1( column2 ), -- aggregate functions containing any columns as arguments,
+ Aggr_Func2( key_n + column2 ), -- including named expressions specified in GROUP BY
+ ...
+FROM table
+GROUP BY
+ column1, column2, ...,
+ <expr> AS key_n -- When grouping by expression, you can set a name for it using AS,
+ -- and use it in SELECT
+```
+
+The query in the format `SELECT * FROM table GROUP BY k1, k2, ...` returns all columns listed in GROUP BY, i.e., is equivalent to `SELECT DISTINCT k1, k2, ... FROM table`.
+
+An asterisk can also be used as an argument for the `COUNT` aggregate function. `COUNT(*)` means "the count of rows in the group".
+
+{% note info %}
+
+Aggregate functions ignore `NULL` in their arguments, except for `COUNT`.
+
+{% endnote %}
+
+YQL also provides aggregation factories implemented by the functions [`AGGREGATION_FACTORY`](../builtins/basic.md#aggregationfactory) and [`AGGREGATE_BY`](../builtins/aggregation.md#aggregateby).
+
+### Examples
+
+```yql
+SELECT key, COUNT(*) FROM my_table
+GROUP BY key;
+```
+
+```yql
+SELECT double_key, COUNT(*) FROM my_table
+GROUP BY key + key AS double_key;
+```
+
+```yql
+SELECT
+ double_key, -- OK: A key column
+ COUNT(*) AS group_size, -- OK: COUNT(*)
+ SUM(key + subkey) AS sum1, -- OK: An aggregate function
+ CAST(SUM(1 + 2) AS String) AS sum2, -- OK: An aggregate function with a constant argument
+ SUM(SUM(1) + key) AS sum3, -- ERROR: Nested aggregations are not allowed
+ key AS k1, -- ERROR: Using a non-key column named key without aggregation
+ key * 2 AS dk1, -- ERROR in YQL: using a non-key column named key without aggregation
+FROM my_table
+GROUP BY
+ key * 2 AS double_key,
+ subkey as sk,
+```
+
+{% note warning %}
+
+Specifying a name for a column or expression in `GROUP BY .. AS foo` it is an extension on top of YQL. Such a name becomes visible in `WHERE` despite the fact that filtering by `WHERE` is executed [before](select/where.md) the grouping. For example, if the `T` table includes two columns, `foo` and `bar`, then the query `SELECT foo FROM T WHERE foo > 0 GROUP BY bar AS foo` would actually filter data by the `bar` column from the source table.
+
+{% endnote %}
+
+
+
+## GROUP BY ... SessionWindow() {#session-window}
+
+YQL supports grouping by session. To standard expressions in `GROUP BY`, you can add a special `SessionWindow` function:
+
+```yql
+SELECT
+ user,
+ session_start,
+ SessionStart() AS same_session_start, -- It's same as session_start
+ COUNT(*) AS session_size,
+ SUM(value) AS sum_over_session,
+FROM my_table
+GROUP BY user, SessionWindow(<time_expr>, <timeout_expr>) AS session_start
+```
+
+The following happens in this case:
+
+1. The input table is partitioned by the grouping keys specified in `GROUP BY`, ignoring SessionWindow (in this case, it's based on `user`).
+
+ If `GROUP BY` includes nothing more than SessionWindow, then the input table gets into one partition.
+
+2. Each partition is split into disjoint subsets of rows (sessions).
+
+ For this, the partition is sorted in the ascending order of the `time_expr` expression.
+ The session limits are drawn between neighboring items of the partition, that differ in their `time_expr` values by more than `timeout_expr`.
+
+3. The sessions obtained in this way are the final partitions on which aggregate functions are calculated.
+
+The `SessionWindow()` key column (in the example, it's `session_start`) has the value "the minimum `time_expr` in the session".
+If `GROUP BY` includes SessionWindow(), you can use a special aggregate function
+[SessionStart](../builtins/aggregation.md#session-start).
+
+An extended version of SessionWindow with four arguments is also supported:
+
+`SessionWindow(<order_expr>, <init_lambda>, <update_lambda>, <calculate_lambda>)`
+
+Where:
+
+* `<order_expr>`: An expression used to sort the source partition
+* `<init_lambda>`: A lambda function to initialize the state of session calculation. It has the signature `(TableRow())->State`. It's called once for the first (following the sorting order) element of the source partition
+* `<update_lambda>`: A lambda function to update the status of session calculation and define the session limits. It has the signature `(TableRow(), State)->Tuple<Bool, State>`. It's called for every item of the source partition, except the first one. The new value of state is calculated based on the current row of the table and the previous state. If the first item in the return tuple is `True`, then a new session starts from the _current_ row. The key of the new session is obtained by applying `<calculate_lambda>` to the second item in the tuple.
+* `<calculate_lambda>`: A lambda function for calculating the session key (the "value" of SessionWindow() that is also accessible via SessionStart()). The function has the signature `(TableRow(), State)->SessionKey`. It's called for the first item in the partition (after `<init_lambda>`) and those items for which `<update_lambda>` has returned `True` in the first item in the tuple. Please note that to start a new session, you should make sure that `<calculate_lambda>` has returned a value different from the previous session key. Sessions having the same keys are not merged. For example, if `<calculate_lambda>` returns the sequence `0, 1, 0, 1`, then there will be four different sessions.
+
+Using the extended version of SessionWindow, you can, for example, do the following: divide a partition into sessions, as in the SessionWindow use case with two arguments, but with the maximum session length limited by a certain constant:
+
+### Example
+
+```yql
+$max_len = 1000; -- is the maximum session length.
+$timeout = 100; -- is the timeout (timeout_expr in a simplified version of SessionWindow).
+
+$init = ($row) -> (AsTuple($row.ts, $row.ts)); -- is the session status: tuple from 1) value of the temporary column ts in the session's first line and 2) in the current line
+$update = ($row, $state) -> {
+ $is_end_session = $row.ts - $state.0 > $max_len OR $row.ts - $state.1 > $timeout;
+ $new_state = AsTuple(IF($is_end_session, $row.ts, $state.0), $row.ts);
+ return AsTuple($is_end_session, $new_state);
+};
+$calculate = ($row, $state) -> ($row.ts);
+SELECT
+ user,
+ session_start,
+ SessionStart() AS same_session_start, -- It's same as session_start
+ COUNT(*) AS session_size,
+ SUM(value) AS sum_over_session,
+FROM my_table
+GROUP BY user, SessionWindow(ts, $init, $update, $calculate) AS session_start
+```
+
+You can use `SessionWindow` in `GROUP BY` only once.
+
+## ROLLUP, CUBE, and GROUPING SETS {#rollup}
+
+The results of calculating the aggregate function as subtotals for the groups and overall totals over individual columns or whole table.
+
+### Syntax
+
+```yql
+SELECT
+ c1, c2, -- the columns to group by
+
+AGGREGATE_FUNCTION(c3) AS outcome_c -- an aggregate function (SUM, AVG, MIN, MAX, COUNT)
+
+FROM table_name
+
+GROUP BY
+ GROUP_BY_EXTENSION(c1, c2) -- an extension of GROUP BY: ROLLUP, CUBE, or GROUPING SETS
+```
+
+* `ROLLUP` groups the column values in the order they are listed in the arguments (strictly from left to right), generates subtotals for each group and the overall total.
+* `CUBE` groups the values for every possible combination of columns, generates the subtotals for each group and the overall total.
+* `GROUPING SETS` sets the groups for subtotals.
+
+You can combine `ROLLUP`, `CUBE` and `GROUPING SETS`, separating them by commas.
+
+### GROUPING {#grouping}
+
+The values of columns not used in calculations are replaced with `NULL` in the subtotal. In the overall total, the values of all columns are replaced by `NULL`. `GROUPING`: A function that allows you to distinguish the source `NULL` values from the `NULL` values added while calculating subtotals and overall totals.
+
+`GROUPING` returns a bit mask:
+
+* `0`: If `NULL` is used for the original empty value.
+* `1`: If `NULL` is added for a subtotal or overall total.
+
+### Example
+
+```yql
+SELECT
+ column1,
+ column2,
+ column3,
+
+ CASE GROUPING(
+ column1,
+ column2,
+ column3,
+ )
+ WHEN 1 THEN "Subtotal: column1 and column2"
+ WHEN 3 THEN "Subtotal: column1"
+ WHEN 4 THEN "Subtotal: column2 and column3"
+ WHEN 6 THEN "Subtotal: column3"
+ WHEN 7 THEN "Grand total"
+ ELSE "Individual group"
+ END AS subtotal,
+
+ COUNT(*) AS rows_count
+
+FROM my_table
+
+GROUP BY
+ ROLLUP(
+ column1,
+ column2,
+ column3
+ ),
+ GROUPING SETS(
+ (column2, column3),
+ (column3)
+ -- if you add here (column2) as well, then together
+ -- the ROLLUP and GROUPING SETS would produce a result
+ -- similar to CUBE
+ )
+;
+```
+
+## DISTINCT {#distinct}
+
+Applying [aggregate functions](../builtins/aggregation.md) only to distinct values of the column.
+
+{% note info %}
+
+Applying `DISTINCT` to calculated values is not currently implemented. For this purpose, you can use a [subquery](select/from.md) or the expression `GROUP BY ... AS ...`.
+
+{% endnote %}
+
+### Example
+
+```yql
+SELECT
+ key,
+ COUNT (DISTINCT value) AS count -- top 3 keys by the number of unique values
+FROM my_table
+GROUP BY key
+ORDER BY count DESC
+LIMIT 3;
+```
+
+You can also use `DISTINCT` to fetch distinct rows using [`SELECT DISTINCT`](select/distinct.md).
+
+## GROUP COMPACT BY
+
+Improves aggregation efficiency if the query author knows in advance that none of aggregation keys finds large amounts of data (i.e., with the order of magnitude exceeding a gigabyte or a million of rows). If this assumption fails to materialize, then the operation may fail with Out of Memory error or start running much slower compared to the non-COMPACT version.
+
+Unlike the usual GROUP BY, the Map-side combiner stage and additional Reduce are disabled for each field with [DISTINCT](../syntax/group_by.md#distinct) aggregation.
+
+### Example
+
+```yql
+SELECT
+ key,
+ COUNT (DISTINCT value) AS count -- top 3 keys by the number of unique values
+FROM my_table
+GROUP COMPACT BY key
+ORDER BY count DESC
+LIMIT 3;
+```
+
+## HAVING {#having}
+
+Filtering a `SELECT` based on the calculation results of [aggregate functions](../builtins/aggregation.md). The syntax is similar to [WHERE](select/where.md).
+
+### Example
+
+```yql
+SELECT
+ key
+FROM my_table
+GROUP BY key
+HAVING COUNT(value) > 100;
+```
diff --git a/yql/essentials/docs/en/syntax/index.md b/yql/essentials/docs/en/syntax/index.md
new file mode 100644
index 00000000000..cd9ca6d592a
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/index.md
@@ -0,0 +1,22 @@
+# List of articles on YQL syntax
+
+* [Lexical structure](lexer.md)
+* [Expressions](expressions.md)
+* [ACTION](action.md)
+* [COMMIT](commit.md)
+* [DECLARE](declare.md)
+* [DISCARD](discard.md)
+* [EXPORT and IMPORT](export_import.md)
+* [FLATTEN](flatten.md)
+* [GROUP BY](group_by.md)
+* [INSERT](insert_into.md)
+* [INTO RESULT](into_result.md)
+* [JOIN](join.md)
+* [PRAGMA](pragma.md)
+* [PROCESS](process.md)
+* [REDUCE](reduce.md)
+* [SELECT](select/index.md)
+* [SUBQUERY](subquery.md)
+* [USE](use.md)
+* [VALUES](values.md)
+* [WINDOW](window.md)
diff --git a/yql/essentials/docs/en/syntax/insert_into.md b/yql/essentials/docs/en/syntax/insert_into.md
new file mode 100644
index 00000000000..36b1287cdd7
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/insert_into.md
@@ -0,0 +1,33 @@
+# INSERT INTO
+
+Adds rows to the table.
+
+The table is searched by name in the database specified by the [USE](use.md) operator.
+
+`INSERT INTO` lets you perform the following operations:
+
+* Adding constant values using [`VALUES`](values.md).
+
+ ```yql
+ INSERT INTO my_table (Key1, Key2, Value1, Value2)
+ VALUES (345987,'ydb', 'Pied piper', 1414);
+ COMMIT;
+ ```
+
+ ```yql
+ INSERT INTO my_table (key, value)
+ VALUES ("foo", 1), ("bar", 2);
+ ```
+
+* Saving the `SELECT` result.
+
+ ```yql
+ INSERT INTO my_table
+ SELECT Key AS Key1, "Empty" AS Key2, Value AS Value1
+ FROM my_table1;
+ ```
+
+Inserts can be made with one or more modifiers. A modifier is specified after the `WITH` keyword following the table name: `INSERT INTO ... WITH SOME_HINT`.
+If a modifier has a value, it's indicated after the `=` sign: `INSERT INTO ... WITH SOME_HINT=value`.
+If necessary, specify multiple modifiers, they should be enclosed in parentheses: `INSERT INTO ... WITH (SOME_HINT1=value, SOME_HINT2, SOME_HINT3=value)`.
+
diff --git a/yql/essentials/docs/en/syntax/into_result.md b/yql/essentials/docs/en/syntax/into_result.md
new file mode 100644
index 00000000000..bb79ec7dae2
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/into_result.md
@@ -0,0 +1,16 @@
+# INTO RESULT
+
+Lets you set a custom label for [SELECT](select/index.md), [PROCESS](process.md), or [REDUCE](reduce.md). It can't be used along with [DISCARD](discard.md).
+
+## Examples
+
+```yql
+SELECT 1 INTO RESULT foo;
+```
+
+```yql
+SELECT * FROM
+my_table
+WHERE value % 2 == 0
+INTO RESULT `Result name`;
+```
diff --git a/yql/essentials/docs/en/syntax/join.md b/yql/essentials/docs/en/syntax/join.md
new file mode 100644
index 00000000000..14ec1ce566e
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/join.md
@@ -0,0 +1,143 @@
+# JOIN
+
+It lets you combine multiple data sources (subqueries or tables) by equality of values in the specified columns or expressions (the `JOIN` keys).
+
+## Syntax
+
+```yql
+SELECT ... FROM table_1
+-- first JOIN step:
+ <Join_Type> JOIN table_2 <Join_Condition>
+ -- left subquery -- entries in table_1
+ -- right subquery -- entries in table_2
+-- next JOIN step:
+ <Join_Type> JOIN table_n <Join_Condition>
+ -- left subquery -- JOIN result in the previous step
+ -- right subquery -- entries in table_n
+-- JOIN can include the following steps
+...
+WHERE ...
+```
+
+At each JOIN step, rules are used to establish correspondences between rows in the left and right data subqueries, creating a new subquery that includes every combination of rows that meet the JOIN conditions.
+
+{% note warning "Attention!" %}
+
+Since columns in YQL are identified by their names, and you can't have two columns with the same name in the subquery, `SELECT * FROM ... JOIN ...` can't be executed if there are columns with identical names in the joined tables.
+
+{% endnote %}
+
+## Types of join
+
+* `INNER` <span style="color: gray;">(default)</span>: Rows from joined subqueries that don't match any rows on the other side won't be included in the result.
+* `LEFT`: If there's no value in the right subquery, it adds a row to the result with column values from the left subquery, using `NULL` in columns from the right subquery
+* `RIGHT`: If there's no value in the left subquery, it adds the row to the result, including column values from the right subquery, but using `NULL` in columns from the left subquery
+* `FULL` = `LEFT` + `RIGHT`
+* `LEFT/RIGHT SEMI`: One side of the subquery is a whitelist of keys, its values are not available. The result includes columns from one table only, no Cartesian product is created.
+* `LEFT/RIGHT ONLY`: Subtracting the sets by keys (blacklist). It's almost the same as adding `IS NULL` to the key on the opposite side in the regular `LEFT/RIGHT` JOIN, but with no access to values: the same as `SEMI` JOIN.
+* `CROSS`: A full Cartesian product of two tables without specifying key columns and no explicit `ON/USING`.
+* `EXCLUSION`: Both sides minus the intersection.
+
+![JOIN](_assets/join-YQL-06.png)
+
+{% note info %}
+
+`NULL` is a special value to denote nothing. Hence, `NULL` values on both sides are not treated as equal to each other. This eliminates ambiguity in some types of `JOIN` and avoids a giant Cartesian product otherwise created.
+
+{% endnote %}
+
+## Conditions for joining
+
+For `CROSS JOIN`, no join condition is specified. The result includes the Cartesian product of the left and right subquery, meaning it combines everything with everything. The number of rows in the resulting subquery is the product of the number of rows in the left and right subqueries.
+
+For any other JOIN types, specify the condition using one of the two methods:
+
+1. `USING (column_name)`. Used if both the left and right subqueries share a column whose equality of values is a join condition.
+2. `ON (equality_conditions)`. Lets you set a condition of equality for column values or expressions over columns in the left and right subqueries or use several such conditions combined by `and`.
+
+### Examples
+
+```yql
+SELECT a.value as a_value, b.value as b_value
+FROM a_table AS a
+FULL JOIN b_table AS b USING (key);
+```
+
+```yql
+SELECT a.value as a_value, b.value as b_value
+FROM a_table AS a
+FULL JOIN b_table AS b ON a.key = b.key;
+```
+
+```yql
+SELECT a.value as a_value, b.value as b_value, c.column2
+FROM a_table AS a
+CROSS JOIN b_table AS b
+LEFT JOIN c_table AS c ON c.ref = a.key and c.column1 = b.value;
+```
+
+To make sure no full scan of the right joined table is required, a secondary index can be applied to the columns included in the Join condition. Accessing a secondary index should be specified explicitly in `JOIN table_name VIEW index_name AS table_alias` format.
+
+For example, creating an index to use in the Join condition:
+
+```yql
+ALTER TABLE b_table ADD INDEX b_index_ref GLOBAL ON(ref);
+```
+
+Using the created index:
+
+```yql
+SELECT a.value as a_value, b.value as b_value
+FROM a_table AS a
+INNER JOIN b_table VIEW b_index_ref AS b ON a.ref = b.ref;
+```
+
+If the statement filters data in addition to `JOIN`, we recommend wrapping the criteria that would return `true` for most of the rows in the `LIKELY(...)` function call. If your assumption that positive values prevail for the criteria is correct, such a hint might speed up your subquery. `LIKELY` can be useful when the predicate calculation is a resource-intensive operation and JOIN significantly reduces the number of rows.
+
+In front of any data source for `JOIN`, you can add the `ANY` keyword to suppress duplicate `JOIN` keys on the given side. In this case, only one row is left from the set of rows with the same `JOIN` key value (no matter which one, that's why the keyword is called `ANY`).
+This syntax is different from that used in [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/select/join/), where `ANY` is placed before the `JOIN` type and applies to the right side only.
+
+Query:
+
+```yql
+$t1 = AsList(
+ AsStruct("1" AS key, "v111" AS value),
+ AsStruct("2" AS key, "v121" AS value),
+ AsStruct("2" AS key, "v122" AS value),
+ AsStruct("3" AS key, "v131" AS value),
+ AsStruct("3" AS key, "v132" AS value));
+
+$t2 = AsList(
+ AsStruct("2" AS key, "v221" AS value),
+ AsStruct("2" AS key, "v222" AS value),
+ AsStruct("3" AS key, "v231" AS value),
+ AsStruct("3" AS key, "v232" AS value),
+ AsStruct("4" AS key, "v241" AS value));
+
+SELECT
+ a.key, a.value, b.value
+FROM ANY AS_TABLE($t1) AS a
+JOIN ANY AS_TABLE($t2) AS b
+ON a.key == b.key;
+```
+
+results in:
+
+| a.key | a.value | b.value |
+| --- | --- | --- |
+| "3" | "v131" | "v231" |
+| "2" | "v121" | "v221" |
+
+and without `ANY` it would result in:
+
+| a.key | a.value | b.value |
+| --- | --- | --- |
+| "3" | "v131" | "v231" |
+| "3" | "v131" | "v232" |
+| "3" | "v132" | "v231" |
+| "3" | "v132" | "v232" |
+| "2" | "v121" | "v221" |
+| "2" | "v121" | "v222" |
+| "2" | "v122" | "v221" |
+| "2" | "v122" | "v222" |
+
diff --git a/yql/essentials/docs/en/syntax/lexer.md b/yql/essentials/docs/en/syntax/lexer.md
new file mode 100644
index 00000000000..9e57b15d646
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/lexer.md
@@ -0,0 +1,235 @@
+
+# Lexical structure
+
+The query in the YQL language is a valid UTF-8 text consisting of **statements** separated by semicolons (`;`).
+The last semicolon can be omitted.
+Each command is a sequence of **tokens** that are valid for this command.
+Tokens can be **keywords**, **identifiers**, **literals**, and so on.
+Tokens are separated by whitespace characters (space, tab, line feed) or **comments**. The comment is not a part of the command and is syntactically equivalent to a space character.
+
+## Syntax compatibility modes {#lexer-modes}
+
+Two syntax compatibility modes are supported:
+
+* Advanced C++ (default)
+* ANSI SQL
+
+ANSI SQL mode is enabled with a special comment `--!ansi_lexer`, which must be in the beginning of the query.
+
+Specifics of interpretation of lexical elements in different compatibility modes are described below.
+
+## Comments {#comments}
+
+The following types of comments are supported:
+
+* Single-line comment: starts with `--` (two minus characters following one another) and continues to the end of the line
+* Multiline comment: starts with `/*` and ends with `*/`
+
+```yql
+SELECT 1; -- A single-line comment
+/*
+ Some multi-line comment
+*/
+```
+
+In C++ syntax compatibility mode (default), a multiline comment ends with the **nearest** `*/`.
+The ANSI SQL syntax compatibility mode accounts for nesting of multiline comments:
+
+```yql
+--!ansi_lexer
+SELECT * FROM T; /* this is a comment /* this is a nested comment, without ansi_lexer it raises an error */ */
+```
+
+## Keywords and identifiers {#keywords-and-ids}
+
+**Keywords** are tokens that have a fixed value in the YQL language. Examples of keywords: `SELECT`, `INSERT`, `FROM`, `ACTION`, and so on. Keywords are case-insensitive, that is, `SELECT` and `SeLEcT` are equivalent to each other.
+The list of keywords is not fixed and is going to expand as the language develops. A keyword can't contain numbers and begin or end with an underscore.
+
+**Identifiers** are tokens that identify the names of tables, columns, and other objects in YQL. Identifiers in YQL are always case-sensitive.
+An identifier can be written in the body of the program without any special formatting, if the identifier:
+
+* Is not a keyword
+* Begins with a Latin letter or underscore
+* Is followed by a Latin letter, an underscore, or a number
+
+```yql
+SELECT my_column FROM my_table; -- my_column and my_table are identifiers
+```
+
+To include an arbitrary ID in the body of a query, the ID is enclosed in backticks:
+
+```yql
+SELECT `column with space` from T;
+SELECT * FROM `my_dir/my_table`
+```
+
+IDs in backticks are never interpreted as keywords:
+
+```yql
+SELECT `select` FROM T; -- select - Column name in the T table
+```
+
+When using backticks, you can use the standard C escaping:
+
+```yql
+SELECT 1 as `column with\n newline, \x0a newline and \` backtick `;
+```
+
+In ANSI SQL syntax compatibility mode, arbitrary IDs can also be enclosed in double quotes. To include a double quote in a quoted ID, use two double quotes:
+
+```yql
+--!ansi_lexer
+SELECT 1 as "column with "" double quote"; -- column name will be: column with " double quote
+```
+
+## SQL hints {#sql-hints}
+
+SQL hints are special settings with which a user can modify a query execution plan
+(for example, enable/disable specific optimizations or force the JOIN execution strategy).
+Unlike [PRAGMA](pragma.md), SQL hints act locally – they are linked to a specific point in the YQL query (normally, after the keyword)
+and affect only the corresponding statement or even a part of it.
+SQL hints are a set of settings "name-value list" and defined inside special comments —
+comments with SQL hints must have `+` as the first character:
+
+```yql
+--+ Name1(Value1 Value2 Value3) Name2(Value4) ...
+```
+
+An SQL hint name must be comprised of ASCII alphanumeric characters and start with a letter. Hint names are case insensitive.
+A hint name must be followed by a custom number of space-separated values. A value can be a custom set of characters.
+If there's a space or parenthesis in a set of characters, single quotation marks must be used:
+
+```yql
+--+ foo('value with space and paren)')
+```
+
+```yql
+--+ foo('value1' value2)
+-- equivalent to
+--+ foo(value1 value2)
+```
+
+To escape a single quotation within a value, double it:
+
+```yql
+--+ foo('value with single quote '' inside')
+```
+
+If there're two or more hints with the same name in the list, the latter is used:
+
+```yql
+--+ foo(v1 v2) bar(v3) foo()
+-- equivalent to
+--+ bar(v3) foo()
+```
+
+Unknown SQL hint names (or syntactically incorrect hints) never result in errors, they're simply ignored:
+
+```yql
+--+ foo(value1) bar(value2 baz(value3)
+-- due to a missing closing parenthesis in bar, is equivalent to
+--+ foo(value1)
+```
+
+Thanks to this behavior, previous valid YQL queries with comments that look like hints remain intact.
+Syntactically correct SQL hints in a place unexpected for YQL result in a warning:
+
+```yql
+-- presently, hints after SELECT are not supported
+SELECT /*+ foo(123) */ 1; -- warning 'Hint foo will not be used'
+```
+
+What's important is that SQL hints are hints for an optimizer, so:
+
+* Hints never affect search results.
+* As YQL optimizers improve, a situation is possible when a hint becomes outdated and is ignored (for example, the algorithm based on a given hint completely changes or the optimizer becomes so sophisticated that it can be expected to choose the best solution, so some manual settings are likely to interfere).
+
+## String literals {#string-literals}
+
+A string literal (constant) is expressed as a sequence of characters enclosed in single quotes. Inside a string literal, you can use the C-style escaping rules:
+
+```yql
+SELECT 'string with\n newline, \x0a newline and \' backtick ';
+```
+
+In the C++ syntax compatibility mode (default), you can use double quotes instead of single quotes:
+
+```yql
+SELECT "string with\n newline, \x0a newline and \" backtick ";
+```
+
+In ASNI SQL compatibility mode, double quotes are used for IDs, and the only escaping that can be used for string literals is a pair of single quotes:
+
+```yql
+--!ansi_lexer
+SELECT 'string with '' quote'; -- result: string with ' quote
+```
+
+Based on string literals, [simple literals](builtins/basic#data-type-literals) can be obtained.
+
+### Multi-line string literals {#multiline-string-literals}
+
+A multiline string literal is expressed as an arbitrary set of characters enclosed in double at signs `@@`:
+
+```yql
+$text = @@some
+multiline
+text@@;
+SELECT LENGTH($text);
+```
+
+If you need to use double at signs in your text, duplicate them:
+
+```yql
+$text = @@some
+multiline with double at: @@@@
+text@@;
+SELECT $text;
+```
+
+### Typed string literals {#typed-string-literals}
+
+* For string literals, including [multi-string](#multiline-string-literals) ones, the `String` type is used by default (see also [PRAGMA UnicodeLiterals](pragma.md#UnicodeLiterals)).
+* You can use the following suffixes to explicitly control the literal type:
+
+ * `s` — `String`
+ * `u` — `Utf8`
+ * `y` — `Yson`
+ * `j` — `Json`
+
+#### Example
+
+```yql
+SELECT "foo"u, '[1;2]'y, @@{"a":null}@@j;
+```
+
+## Numeric literals {#literal-numbers}
+
+* Integer literals have the default type `Int32`, if they fit within the Int32 range. Otherwise, they automatically expand to `Int64`.
+* You can use the following suffixes to explicitly control the literal type:
+
+ * `l`: `Int64`
+ * `s`: `Int16`
+ * `t`: `Int8`
+
+* Add the suffix `u` to convert a type to its corresponding unsigned type:
+
+ * `ul`: `Uint64`
+ * `u`: `Uint32`
+ * `us`: `Uint16`
+ * `ut`: `Uint8`
+
+* You can also use hexadecimal, octal, and binary format for integer literals using the prefixes `0x`, `0o` and `0b`, respectively. You can arbitrarily combine them with the above-mentioned suffixes.
+* Floating point literals have the `Double` type by default, but you can use the suffix `f` to narrow it down to `Float`.
+
+```yql
+SELECT
+ 123l AS `Int64`,
+ 0b01u AS `Uint32`,
+ 0xfful AS `Uint64`,
+ 0o7ut AS `Uint8`,
+ 456s AS `Int16`,
+ 1.2345f AS `Float`;
+```
+
+
diff --git a/yql/essentials/docs/en/syntax/not_yet_supported.md b/yql/essentials/docs/en/syntax/not_yet_supported.md
new file mode 100644
index 00000000000..f6ebbc1d592
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/not_yet_supported.md
@@ -0,0 +1,14 @@
+# Classic SQL constructs not supported yet
+
+## \[NOT\] \[EXISTS|INTERSECT\|EXCEPT] {#not-exists}
+
+A syntactically available alternative is `EXISTS`, but it's not very useful as it doesn't support correlated subqueries. You can also rewrite it using `JOIN`.
+
+## NATURAL JOIN {#natural-join}
+
+An alternative is to explicitly list the matching columns on both sides.
+
+## NOW() / CURRENT_TIME() {#now}
+
+An alternative is to use the functions [CurrentUtcDate, CurrentUtcDatetime and CurrentUtcTimestamp](../builtins/basic.md#current-utc).
+
diff --git a/yql/essentials/docs/en/syntax/pragma.md b/yql/essentials/docs/en/syntax/pragma.md
new file mode 100644
index 00000000000..afbfe502271
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/pragma.md
@@ -0,0 +1,421 @@
+# PRAGMA
+
+## Definition
+
+Redefinition of settings.
+
+### Syntax
+
+`PRAGMA x.y = "z";` or `PRAGMA x.y("z", "z2", "z3");`:
+
+* `x`: (optional) The category of the setting.
+* `y`: The name of the setting.
+* `z`: (optional for flags) The value of the setting. The following suffixes are acceptable:
+
+ * `Kb`, `Mb`, `Gb`: For the data amounts.
+ * `sec`, `min`, `h`, `d`: For the time values.
+
+### Examples
+
+```yql
+PRAGMA AutoCommit;
+```
+
+```yql
+PRAGMA TablePathPrefix = "home/yql";
+```
+
+```yql
+PRAGMA Warning("disable", "1101");
+```
+
+With some exceptions, you can return the settings values to their default states using `PRAGMA my_pragma = default;`.
+
+For the full list of available settings, [see the table below](pragma.md#pragmas).
+
+### Scope {#pragmascope}
+
+Unless otherwise specified, a pragma affects all the subsequent expressions up to the end of the module where it's used. If necessary and logically possible, you can change the value of this setting several times within a given query to make it different at different execution steps.
+
+There are also special scoped pragmas with the scope defined by the same rules as the scope of [named expressions](expressions.md#named-nodes). Unlike scoped pragmas, regular pragmas can only be used in the global scope (not inside lambda functions, `ACTION`, `SUBQUERY`, etc.).
+
+
+
+## Global {#pragmas}
+
+### AutoCommit {#autocommit}
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+Automatically run [COMMIT](commit.md) after every statement.
+
+### TablePathPrefix {#table-path-prefix}
+
+| Value type | Default |
+| --- | --- |
+| String | — |
+
+Add the specified prefix to the cluster table paths. It uses standard file system path concatenation, supporting parent folder `..` referencing and requiring no trailing slash. For example,
+
+`PRAGMA TablePathPrefix = "home/yql";
+SELECT * FROM test;`
+
+The prefix is not added if the table name is an absolute path (starts with /).
+
+### UseTablePrefixForEach {#use-table-prefix-for-each}
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+EACH uses [TablePathPrefix](#table-path-prefix) for each list item.
+
+### Warning {#warning}
+
+| Value type | Default |
+| --- | --- |
+| 1. Action<br/>2. Warning code or "*" | — |
+
+Action:
+
+* `disable`: Disable.
+* `error`: Treat as an error.
+* `default`: Revert to the default behavior.
+
+The warning code is returned with the text itself (it's displayed on the right side of the web interface).
+
+#### Example
+
+`PRAGMA Warning("error", "*");`
+`PRAGMA Warning("disable", "1101");`
+`PRAGMA Warning("default", "4503");`
+
+In this case, all the warnings are treated as errors, except for the warning `1101` (that will be disabled) and `4503` (that will be processed by default, that is, remain a warning). Since warnings may be added in new YQL releases, use `PRAGMA Warning("error", "*");` with caution (at least cover such queries with autotests).
+
+### SimpleColumns {#simplecolumns}
+
+`SimpleColumns` / `DisableSimpleColumns`
+
+| Value type | Default |
+| --- | --- |
+| Flag | true |
+
+When you use `SELECT foo.* FROM ... AS foo`, remove the `foo.` prefix from the names of the result columns.
+
+It can be also used with a [JOIN](join.md), but in this case it may fail in the case of a name conflict (that can be resolved by using [WITHOUT](select/without.md) and renaming columns). For JOIN in SimpleColumns mode, an implicit Coalesce is made for key columns: the query `SELECT * FROM T1 AS a JOIN T2 AS b USING(key)` in the SimpleColumns mode works same as `SELECT a.key ?? b.key AS key, ... FROM T1 AS a JOIN T2 AS b USING(key)`.
+
+### CoalesceJoinKeysOnQualifiedAll
+
+`CoalesceJoinKeysOnQualifiedAll` / `DisableCoalesceJoinKeysOnQualifiedAll`
+
+| Value type | Default |
+| --- | --- |
+| Flag | true |
+
+Controls implicit Coalesce for the key `JOIN` columns in the SimpleColumns mode. If the flag is set, the Coalesce is made for key columns if there is at least one expression in the format `foo.*` or `*` in SELECT: for example, `SELECT a.* FROM T1 AS a JOIN T2 AS b USING(key)`. If the flag is not set, then Coalesce for JOIN keys is made only if there is an asterisk '*' after `SELECT`
+
+### StrictJoinKeyTypes
+
+`StrictJoinKeyTypes` / `DisableStrictJoinKeyTypes`
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+If the flag is set, then [JOIN](join.md) will require strict matching of key types.
+By default, JOIN preconverts keys to a shared type, which might result in performance degradation.
+StrictJoinKeyTypes is a [scoped](pragma.md#pragmascope) setting.
+
+### AnsiInForEmptyOrNullableItemsCollections
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+This pragma brings the behavior of the `IN` operator in accordance with the standard when there's `NULL` in the left or right side of `IN`. The behavior of `IN` when on the right side there is a Tuple with elements of different types also changed. Examples:
+
+`1 IN (2, 3, NULL) = NULL (was Just(False))`
+`NULL IN () = Just(False) (was NULL)`
+`(1, null) IN ((2, 2), (3, 3)) = Just(False) (was NULL)`
+
+For more information about the `IN` behavior when operands include `NULL`s, see [here](expressions.md#in). You can explicitly select the old behavior by specifying the pragma `DisableAnsiInForEmptyOrNullableItemsCollections`. If no pragma is set, then a warning is issued and the old version works.
+
+### AnsiRankForNullableKeys
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+Aligns the RANK/DENSE_RANK behavior with the standard if there are optional types in the window sort keys or in the argument of such window functions. It means that:
+
+* The result type is always Uint64 rather than Uint64?.
+* NULLs in keys are treated as equal to each other (the current implementation returns NULL).
+
+You can explicitly select the old behavior by using the `DisableAnsiRankForNullableKeys` pragma. If no pragma is set, then a warning is issued and the old version works.
+
+### AnsiCurrentRow
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+Aligns the implicit setting of a window frame with the standard if there is ORDER BY.
+If AnsiCurrentRow is not set, then the `(ORDER BY key)` window is equivalent to `(ORDER BY key ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)`.
+The standard also requires that this window behave as `(ORDER BY key RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)`.
+The difference is in how `CURRENT ROW` is interpreted. In `ROWS` mode `CURRENT ROW` is interpreted literally: the current row in a partition.
+In `RANGE` mode, the end of the `CURRENT ROW` frame means "the last row in a partition with a sort key equal to the current row".
+
+### AnsiOrderByLimitInUnionAll
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+Aligns the UNION ALL behavior with the standard if there is `ORDER BY/LIMIT/DISCARD/INSERT INTO` in the combined subqueries. It means that:
+
+* `ORDER BY/LIMIT/INSERT INTO` are allowed only after the last subquery.
+* `DISCARD` is allowed only before the first subquery.
+* The specified operators apply to the `UNION ALL` result (unlike the current behavior when they apply only to the subquery).
+* To apply an operator to a subquery, enclose the subquery in parentheses.
+
+You can explicitly select the old behavior by using the `DisableAnsiOrderByLimitInUnionAll` pragma. If no pragma is set, then a warning is issued and the old version works.
+
+### OrderedColumns {#orderedcolumns}
+
+`OrderedColumns`/`DisableOrderedColumns`
+
+Output the [column order](select/order_by.md) in SELECT/JOIN/UNION ALL and preserve it when writing the results. The order of columns is undefined by default.
+
+### PositionalUnionAll {#positionalunionall}
+
+Enable the standard column-by-column execution for [UNION ALL](select/union.md#unionall). This automatically enables
+[ordered columns](#orderedcolumns).
+
+### RegexUseRe2
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+Use Re2 UDF instead of Pcre to execute SQL the `REGEX`,`MATCH`,`RLIKE` statements. Re2 UDF can properly handle Unicode characters, unlike the default Pcre UDF.
+
+### ClassicDivision
+
+| Value type | Default |
+| --- | --- |
+| Flag | true |
+
+In the classical version, the result of integer division remains integer (by default).
+If disabled, the result is always Double.
+ClassicDivision is a [scoped](pragma.md#pragmascope) setting.
+
+### UnicodeLiterals
+
+`UnicodeLiterals`/`DisableUnicodeLiterals`
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+When this mode is enabled, string literals without suffixes like "foo"/'bar'/@@multiline@@ will be of type `Utf8`, when disabled - `String`.
+UnicodeLiterals is a [scoped](pragma.md#pragmascope) setting.
+
+### WarnUntypedStringLiterals
+
+`WarnUntypedStringLiterals`/`DisableWarnUntypedStringLiterals`
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+When this mode is enabled, a warning will be generated for string literals without suffixes like "foo"/'bar'/@@multiline@@. It can be suppressed by explicitly choosing the suffix `s` for the `String` type, or `u` for the `Utf8` type.
+WarnUntypedStringLiterals is a [scoped](pragma.md#pragmascope) setting.
+
+### AllowDotInAlias
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+Enable dot in names of result columns. This behavior is disabled by default, since the further use of such columns in JOIN is not fully implemented.
+
+### WarnUnnamedColumns
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+Generate a warning if a column name was automatically generated for an unnamed expression in `SELECT` (in the format `column[0-9]+`).
+
+### GroupByLimit
+
+| Value type | Default |
+| --- | --- |
+| Positive number | 32 |
+
+Increasing the limit on the number of dimensions in [GROUP BY](group_by.md).
+
+### GroupByCubeLimit
+
+| Value type | Default |
+| --- | --- |
+| Positive number | 5 |
+
+Increasing the limit on the number of dimensions in [GROUP BY](group_by.md#rollup-cube-group-sets).
+
+Use this option with care, because the computational complexity of the query grows exponentially with the number of dimensions.
+
+
+## Yson
+
+Managing the default behavior of Yson UDF, for more information, see the [documentation](../udf/list/yson.md) and, in particular, [Yson::Options](../udf/list/yson.md#ysonoptions).
+
+### `yson.AutoConvert`
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+Automatic conversion of values to the required data type in all Yson UDF calls, including implicit calls.
+
+### `yson.Strict`
+
+| Value type | Default |
+| --- | --- |
+| Flag | true |
+
+Strict mode control in all Yson UDF calls, including implicit calls. If the value is omitted or is `"true"`, it enables the strict mode. If the value is `"false"`, it disables the strict mode.
+
+### `yson.DisableStrict`
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+An inverted version of `yson.Strict`. If the value is omitted or is `"true"`, it disables the strict mode. If the value is `"false"`, it enables the strict mode.
+
+
+## Working with files
+
+### File
+
+| Value type | Default | Static/<br/>dynamic |
+| --- | --- | --- |
+| Two string arguments: alias and URL | — | Static |
+
+Attach a file to the query by URL. For attaching files you can use the built-in functions [FilePath and FileContent](../builtins/basic.md#filecontent).
+
+### Folder
+
+| Value type | Default | Static/<br/>dynamic |
+| --- | --- | --- |
+| Two string arguments: prefix and URL | — | Static |
+
+Attach a set of files to the query by URL. Works similarly to adding multiple files via [`PRAGMA File`](#file) by direct links to files with aliases obtained by combining a prefix with a file name using `/`.
+
+### Library
+
+| Value type | Default | Static/<br/>dynamic |
+| --- | --- | --- |
+| One or two arguments: the file name and an optional URL | — | Static |
+
+Treat the specified attached file as a library from which you can do [IMPORT](export_import.md). The syntax type for the library is determined from the file extension:
+
+* `.sql`: For the YQL dialect of SQL <span style="color: green;">(recommended)</span>.
+* `.yql`: For [s-expressions](/docs/s_expressions).
+
+Example with a file attached to the query:
+
+```yql
+PRAGMA library("a.sql");
+IMPORT a SYMBOLS $x;
+SELECT $x;
+```
+
+If the URL is specified, the library is downloaded from the URL rather than from the pre-attached file as in the following example:
+
+```yql
+PRAGMA library("a.sql","http://intranet.site/5618566/text");
+IMPORT a SYMBOLS $x;
+SELECT $x;
+```
+
+In this case, you can use text parameter value substitution in the URL:
+
+```yql
+DECLARE $_ver AS STRING; -- "5618566"
+PRAGMA library("a.sql","http://intranet.site/{$_ver}/text");
+IMPORT a SYMBOLS $x;
+SELECT $x;
+```
+
+### Package
+
+| Value type | Default | Static /<br/>dynamic |
+| --- | --- | --- |
+| Two or three arguments: package name, URL and optional token | — | Static |
+
+Attach a hierarchical set of files to the query by URL, treating them as a package with a given name - an interconnected set of libraries.
+
+Package name is expected to be given as ``project_name.package_name``; from package's libraries you can do [IMPORT](export_import.md) with a module name like ``pkg.project_name.package_name.maybe.nested.module.name``.
+
+Example for a package with flat hierarchy which consists of two libraries - foo.sql and bar.sql:
+
+```yql
+PRAGMA package("project.package", "http://intranet.site/path/to/package");
+IMPORT pkg.project.package.foo SYMBOLS $foo;
+IMPORT pkg.project.package.bar SYMBOLS $bar;
+SELECT $foo, $bar;
+```
+
+You can also use text parameter value substitution in the URL:
+
+```yql
+DECLARE $_path AS STRING; -- "path"
+PRAGMA package("project.package","http://intranet.site/{$_path}/to/package");
+IMPORT pkg.project.package.foo SYMBOLS $foo;
+IMPORT pkg.project.package.bar SYMBOLS $bar;
+SELECT $foo, $bar;
+```
+
+### OverrideLibrary
+
+| Value type | Default | Static/<br/>dynamic |
+| --- | --- | --- |
+| One argument: the file name | — | Static |
+
+Treat the specified attached file as a library and override with it one of package's libraries.
+
+File name is expected to be given as ``project_name/package_name/maybe/nested/module/name.EXTENSION``, extensions analagous to [PRAGMA Library](#library) are supported.
+
+Example:
+
+```yql
+PRAGMA package("project.package", "http://intranet.site/path/to/package");
+PRAGMA override_library("project/package/maybe/nested/module/name.sql");
+
+IMPORT pkg.project.package.foo SYMBOLS $foo;
+SELECT $foo;
+```
+
+## Debugging and auxiliary settings {#debug}
+
+
+### `config.flags("ValidateUdf", "Lazy")`
+
+| Value type | Default |
+| --- | --- |
+| String: None/Lazy/Greedy | None |
+
+Validating whether UDF results match the declared signature. The Greedy mode enforces materialization of lazy containers, although the Lazy mode doesn't.
+
+### `config.flags("Diagnostics")`
+
+| Value type | Default |
+| --- | --- |
+| Flag | false |
+
+Getting diagnostic information from YQL as an additional result of a query.
+
diff --git a/yql/essentials/docs/en/syntax/process.md b/yql/essentials/docs/en/syntax/process.md
new file mode 100644
index 00000000000..e6f18e07f94
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/process.md
@@ -0,0 +1,78 @@
+# PROCESS
+
+Convert the input table using a UDF or a [lambda function](expressions.md#lambda) that is applied sequentially to each input row and can create zero to create one or more result row for each input row (similarly to Map in MapReduce terms).
+
+The parameters of the function call after the `USING` keyword explicitly specify the values of columns from which to pass values for each input row and in which order.
+
+You can use functions that return the result of one of three composite types derived from `OutputType` (supported `OutputType` options are described below):
+
+* `OutputType`: Each input row must always have an output row with the schema determined by the structure type.
+* `OutputType?`: The function may skip rows, returning empty values (`TUnboxedValue()` in C++, `None` in Python, or `null` in JavaScript).
+* `Stream<OutputType>` or `List<OutputType>`: An option to return multiple rows.
+
+Regardless of the option selected above, the result is converted to a flat table with columns defined by the `OutputType` type.
+
+As `OutputType`, you can use one of the types:
+
+* `Struct<...>`: `PROCESS` has exactly one output with entries of a given structure that is a flat table with columns corresponding to the fields `Struct<...>`
+* `Variant<Struct...>,...>`: `PROCESS` will have the number of outputs equal to the number of variants in `Variant`. The entries of each output are represented by a flat table with columns based on fields from the relevant variant. In this case, you can access the set of `PROCESS` outputs as a `Tuple` of lists that can be unpacked into separate [named expressions](expressions.md#named-nodes) and used independently.
+
+In the list of function arguments after the `USING` keyword, you can pass one of the two special named expressions:
+
+* `TableRow()`: The entire current row in the form of a structure.
+* `TableRows()`: A lazy iterator by strings, in terms of the types `Stream<Struct...>>`. In this case, the output type of the function can only be `Stream<OutputType>` or `List<OutputType>`.
+
+{% note info %}
+
+After executing `PROCESS`, within the same query, on the resulting table (or tables), you can perform [SELECT](select/index.md), [REDUCE](reduce.md), other `PROCESS` and so on, depending on the intended result.
+
+{% endnote %}
+
+The `USING` keyword and the function are optional: if you omit them, the source table is returned. This can be convenient for using a [subquery template](subquery.md).
+
+In `PROCESS` you can pass multiple inputs (the input here means a table, a [range of tables](select/concat.md), a subquery, a [named expression](expressions.md#named-nodes)), separated by commas. To the function from `USING`, you can only pass in this case special named expressions `TableRow()` or `TableRows()` that will have the following type:
+
+* `TableRow()`: A `Variant` where each element has an entry structure type from the relevant input. For each input row in the Variant, the element corresponding to the occurrence ID for this row is non-empty
+* `TableRows()`: A lazy iterator by Variants, in terms of the types `Stream<Variant...>>`. The alternative has the same semantics as for `TableRow()`
+
+After `USING` in `PROCESS` you can optionally specify `ASSUME ORDER BY` with a list of columns. The result of such a `PROCESS` statement is treated as sorted, but without actually running a sort. Sort check is performed at the query execution stage. It supports setting the sort order using the keywords `ASC` (ascending order) and `DESC` (descending order). Expressions are not supported in `ASSUME ORDER BY`.
+
+## Examples
+
+```yql
+PROCESS my_table
+USING MyUdf::MyProcessor(value)
+```
+
+```yql
+$udfScript = @@
+def MyFunc(my_list):
+ return [(int(x.key) % 2, x) for x in my_list]
+@@;
+
+-- The function returns an iterator of Variants
+$udf = Python3::MyFunc(Callable<(Stream<Struct<...>>) -> Stream<Variant<Struct<...>, Struct<...>>>>,
+ $udfScript
+);
+
+-- The output of the PROCESS produces a tuple of lists
+$i, $j = (PROCESS my_table USING $udf(TableRows()));
+
+SELECT * FROM $i;
+SELECT * FROM $j;
+```
+
+```yql
+$udfScript = @@
+def MyFunc(stream):
+ for r in stream:
+ yield {"alt": r[0], "key": r[1].key}
+@@;
+
+-- The function accepts an iterator of Variants as input
+$udf = Python::MyFunc(Callable<(Stream<Variant<Struct<...>, Struct<...>>>) -> Stream<Struct<...>>>,
+ $udfScript
+);
+
+PROCESS my_table1, my_table2 USING $udf(TableRows());
+```
diff --git a/yql/essentials/docs/en/syntax/reduce.md b/yql/essentials/docs/en/syntax/reduce.md
new file mode 100644
index 00000000000..e7c579c7ee5
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/reduce.md
@@ -0,0 +1,108 @@
+# REDUCE
+
+Groups the input by the specified key columns, then passes the current keys and a lazy iterator over their corresponding values from the remaining columns to the specified UDF for processing. Similarly to `PROCESS`, the UDF can return an arbitrary number of result rows per call and also return `Variants` to create multiple outputs. In terms of MapReduce, it's very similar to Reduce.
+
+Keywords that can follow:
+
+* `PRESORT` <span style="color: gray;">(optional)</span>: Specifying the order within each group, the syntax is similar to [ORDER BY](select/order_by.md);
+* `ON` <span style="color: gray;">(required)</span>: Specifying key columns.
+* `USING` or `USING ALL` <span style="color: gray;">(required)</span>: A UDF call, see more about the rules below.
+
+Rules for passing UDF arguments:
+
+* If `TableRows()` is specified as a UDF argument, then the UDF must accept one argument: a lazy iterator over strings, with the type `Stream<Struct...>>`. In this case, the output type of the function can only be `Stream<OutputType>` or `List<OutputType>`. It's guaranteed that the data in the input iterator is grouped by the key and, if necessary, sorted according to the `PRESORT` section. With `TableRows()`, you can only use `USING ALL`.
+* With `USING`:
+
+ * The UDF must accept two arguments: the current key is passed to the first argument, and a lazy iterator with values corresponding to this key is passed to the second argument.
+
+* With `USING ALL`:
+
+ * The UDF must accept one argument: a lazy iterator over `Tuples`, where the first item in the tuple is a key, and the second item is a lazy iterator with values corresponding to this key.
+
+* The key to be passed to the UDF follows the rule below. If there is only one key column, then only its value is used in the key. If there are multiple columns (columns are listed similarly to `GROUP BY` separated by commas), then the key is a `Tuple` with values from the listed columns in the specified order.
+* When you call `REDUCE` from a query, only the expression whose values will be passed as iterator items follows the UDF name in parentheses (the second UDF argument for `USING` or the second item of the tuple for `USING ALL`).
+
+The result is built in the same way as for [PROCESS](process.md). You can also use the `TableRow()` keyword to get the whole string as a structure.
+
+In `REDUCE`, you can pass multiple inputs (the input here means a table, a [range of tables](select/concat.md), a subquery, a [named expression](expressions.md#named-nodes)), separated by commas. All inputs must have the key columns of the matching type specified in `ON`. To the function from `USING` in this case, you can only pass a special named expression `TableRow()`. The second argument (or the second element of the tuple for `USING ALL`) will include a lazy iterator over variants with a populated element that corresponds to the occurrence ID for the current entry.
+
+After `USING`, in `REDUCE` you can optionally specify `ASSUME ORDER BY` with a list of columns. The result of such a `REDUCE` statement is treated as sorted, but without actually running a sort. Sort check is performed at the query execution stage. It supports setting the sort order using the keywords `ASC` (ascending order) and `DESC` (descending order). Expressions are not supported in `ASSUME ORDER BY`.
+
+## Examples
+
+```yql
+REDUCE my_table
+ON key, subkey
+USING MyUdf::MyReducer(TableRow());
+```
+
+```yql
+REDUCE my_table
+ON key, subkey
+USING ALL MyUDF::MyStreamReducer(TableRow()); -- MyUDF::MyStreamReducer accepts a lazy list of tuples (key, list of entries for the key) as its input
+```
+
+```yql
+REDUCE my_table
+PRESORT LENGTH(subkey) DESC
+ON key
+USING MyUdf::MyReducer(
+ AsTuple(subkey, value)
+);
+```
+
+```yql
+REDUCE my_table
+ON key
+USING ALL MyUDF::MyFlatStreamReducer(TableRows()); -- MyUDF::MyFlatStreamReducer accepts a single lazy list of entries as its input
+```
+
+```yql
+-- The function returns Variants
+$udf = Python::MyReducer(Callable<(String, Stream<Struct<...>>) -> Variant<Struct<...>, Struct<...>>>,
+ $udfScript
+);
+
+-- The output of REDUCE produces a tuple of lists
+$i, $j = (REDUCE my_table ON key USING $udf(TableRow()));
+
+SELECT * FROM $i;
+SELECT * FROM $j;
+```
+
+```yql
+$script = @@
+def MyReducer(key, values):
+ state = None, 0
+ for name, last_visit_time in values:
+ if state[1] < last_visit_time:
+ state = name, last_visit_time
+ return {
+ 'region':key,
+ 'last_visitor':state[0],
+ }
+@@;
+
+$udf = Python::MyReducer(Callable<(
+ Int64?,
+ Stream<Tuple<String?, Uint64?>>
+) -> Struct<
+ region:Int64?,
+ last_visitor:String?
+>>,
+ $script
+);
+
+REDUCE my_table
+ON region USING $udf((name, last_visit_time));
+```
+
+```yql
+-- The function accepts a key and iterator of Variants as input
+$udf = Python::MyReducer(Callable<(String, Stream<Variant<Struct<...>,Struct<...>>>) -> Struct<...>>,
+ $udfScript
+);
+
+REDUCE my_table1, my_table2 ON key USING $udf(TableRow());
+```
+
diff --git a/yql/essentials/docs/en/syntax/select/assume_order_by.md b/yql/essentials/docs/en/syntax/select/assume_order_by.md
new file mode 100644
index 00000000000..61c9e241963
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/assume_order_by.md
@@ -0,0 +1,14 @@
+# ASSUME ORDER BY
+
+Checking that the `SELECT` result is sorted by the value in the specified column or multiple columns. The result of such a `SELECT` statement is treated as sorted, but without actually running a sort. Sort check is performed at the query execution stage.
+
+As in case of `ORDER BY`, it supports setting the sort order using the keywords `ASC` (ascending order) and `DESC` (descending order). Expressions are not supported in `ASSUME ORDER BY`.
+
+## Examples
+
+```yql
+SELECT key || "suffix" as key, -CAST(subkey as Int32) as subkey
+FROM my_table
+ASSUME ORDER BY key, subkey DESC;
+```
+
diff --git a/yql/essentials/docs/en/syntax/select/concat.md b/yql/essentials/docs/en/syntax/select/concat.md
new file mode 100644
index 00000000000..e1170e72878
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/concat.md
@@ -0,0 +1,44 @@
+## Accessing multiple tables in a single query {#concat} {#each} {#range} {#like} {#filter} {#regexp}
+
+Standard SQL uses [UNION ALL](union.md#union-all) to execute a query across multiple tables and combine the results of two or more `SELECT` statements. This is not very convenient for the use case of running the same query on multiple tables (for example, if they contain data for different dates). To make this more convenient, in YQL `SELECT` statements, after `FROM`, you can specify not only a table or a subquery, but also call built-in functions letting you combine data from multiple tables.
+
+The following functions are defined for these purposes:
+
+```CONCAT(`table1`, `table2`, `table3` VIEW view_name, ...)``` combines all the tables listed in the arguments.
+
+```EACH($list_of_strings) or EACH($list_of_strings VIEW view_name)``` combines all tables whose names are listed in the list of strings. Optionally, you can pass multiple lists in separate arguments similar to `CONCAT`.
+
+{% note warning %}
+
+All of the above functions don't guarantee the order of the table union.
+
+The list of tables is calculated **before** executing the query. Therefore, the tables created during the query execution won't be included in the function results.
+
+{% endnote %}
+
+By default, the schemas of all the unioned tables are combined using the [UNION ALL](union.md#union-all) rules. If you don't want to combine schemas, then you can use functions with the `_STRICT` suffix, for example `CONCAT_STRICT`, that are totally similar to the original functions, but treat any difference in table schemas as an error.
+
+To specify a cluster of unioned tables, add it before the function name.
+
+All arguments of the functions described above can be declared separately using [named expressions](../expressions.md#named-nodes). In this case, you can also use simple expressions in them by implicitly calling [EvaluateExpr](../../builtins/basic.md#evaluate_expr_atom).
+
+To get the name of the source table from which you originally obtained each row, use [TablePath()](../../builtins/basic.md#tablepath).
+
+### Examples
+
+```yql
+USE some_cluster;
+SELECT * FROM CONCAT(
+ `table1`,
+ `table2`,
+ `table3`);
+```
+
+```yql
+USE some_cluster;
+$indices = ListFromRange(1, 4);
+$tables = ListMap($indices, ($index) -> {
+ RETURN "table" || CAST($index AS String);
+});
+SELECT * FROM EACH($tables); -- Identical to the previous example
+```
diff --git a/yql/essentials/docs/en/syntax/select/distinct.md b/yql/essentials/docs/en/syntax/select/distinct.md
new file mode 100644
index 00000000000..ae3eec36856
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/distinct.md
@@ -0,0 +1,19 @@
+# DISTINCT
+
+Selecting unique rows.
+
+{% note info %}
+
+Applying `DISTINCT` to calculated values is not currently implemented. For this purpose, use a subquery or the clause [`GROUP BY ... AS ...`](../group_by.md).
+
+{% endnote %}
+
+## Example
+
+```yql
+SELECT DISTINCT value -- only unique values from the table
+FROM my_table;
+```
+
+The `DISTINCT` keyword can also be used to apply [aggregate functions](../../builtins/aggregation.md) only to distinct values. For more information, see the documentation for [GROUP BY](../group_by.md).
+
diff --git a/yql/essentials/docs/en/syntax/select/from.md b/yql/essentials/docs/en/syntax/select/from.md
new file mode 100644
index 00000000000..924790d9def
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/from.md
@@ -0,0 +1,22 @@
+## FROM {#from}
+
+Data source for `SELECT`. The argument can accept the table name, the result of another `SELECT`, or a [named expression](../expressions.md#named-nodes). Between `SELECT` and `FROM`, list the comma-separated column names from the source (or `*` to select all columns).
+
+The table is searched by name in the database specified by the operator [USE](../use.md).
+
+### Examples
+
+```yql
+SELECT key FROM my_table;
+```
+
+```yql
+SELECT * FROM
+ (SELECT value FROM my_table);
+```
+
+```yql
+$table_name = "my_table";
+SELECT * FROM $table_name;
+```
+
diff --git a/yql/essentials/docs/en/syntax/select/from_as_table.md b/yql/essentials/docs/en/syntax/select/from_as_table.md
new file mode 100644
index 00000000000..459cd51b04b
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/from_as_table.md
@@ -0,0 +1,17 @@
+## FROM AS_TABLE {#as-table}
+
+Accessing named expressions as tables using the `AS_TABLE` function.
+
+`AS_TABLE($variable)` lets you use the value of `$variable` as the data source for the query. In this case, the variable `$variable` must have the type `List<Struct<...>>`.
+
+### Example
+
+```yql
+$data = AsList(
+ AsStruct(1u AS Key, "v1" AS Value),
+ AsStruct(2u AS Key, "v2" AS Value),
+ AsStruct(3u AS Key, "v3" AS Value));
+
+SELECT Key, Value FROM AS_TABLE($data);
+```
+
diff --git a/yql/essentials/docs/en/syntax/select/from_select.md b/yql/essentials/docs/en/syntax/select/from_select.md
new file mode 100644
index 00000000000..76373ca2510
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/from_select.md
@@ -0,0 +1,17 @@
+## FROM ... SELECT ... {#from-select}
+
+An inverted format, first specifying the data source and then the operation.
+
+### Examples
+
+```yql
+FROM my_table SELECT key, value;
+```
+
+```yql
+FROM a_table AS a
+JOIN b_table AS b
+USING (key)
+SELECT *;
+```
+
diff --git a/yql/essentials/docs/en/syntax/select/index.md b/yql/essentials/docs/en/syntax/select/index.md
new file mode 100644
index 00000000000..656e2bfba4e
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/index.md
@@ -0,0 +1,102 @@
+<!-- markdownlint-disable blanks-around-fences -->
+
+# SELECT
+
+Returns the result of evaluating the expressions specified after `SELECT`.
+
+It can be used in combination with other operations to obtain other effect.
+
+## Examples
+
+```yql
+SELECT "Hello, world!";
+```
+
+```yql
+SELECT 2 + 2;
+```
+
+## SELECT execution procedure {#selectexec}
+
+The `SELECT` query result is calculated as follows:
+
+* Determine the set of input tables by evaluating the [FROM](from.md) clauses.
+* Apply [SAMPLE](sample.md)/[TABLESAMPLE](sample.md) to input tables.
+* Execute [FLATTEN COLUMNS](../flatten.md#flatten-columns) or [FLATTEN BY](../flatten.md); aliases set in `FLATTEN BY` become visible after this point.
+* Execute every [JOIN](../join.md).
+* Add to (or replace in) the data the columns listed in [GROUP BY ... AS ...](../group_by.md).
+* Execute [WHERE](where.md) - discard all the data mismatching the predicate.
+* Execute [GROUP BY](../group_by.md), evaluate aggregate functions.
+* Apply the filter [HAVING](../group_by.md#having).
+* Evaluate [window functions](../window.md);
+* Evaluate expressions in `SELECT`.
+* Assign names set by aliases to expressions in `SELECT`.
+* Apply top-level [DISTINCT](distinct.md) to the resulting columns.
+* Execute similarly every subquery inside [UNION ALL](union.md#union-all), combine them (see [PRAGMA AnsiOrderByLimitInUnionAll](../pragma.md#pragmas)).
+* Perform sorting with [ORDER BY](order_by.md).
+* Apply [OFFSET and LIMIT](limit_offset.md) to the result.
+
+
+## Column order in YQL {#orderedcolumns}
+
+The standard SQL is sensitive to the order of columns in projections (that is, in `SELECT`). While the order of columns must be preserved in the query results or when writing data to a new table, some SQL constructs use this order.
+This applies, for example, to [UNION ALL](union.md#union-all) and positional [ORDER BY](order_by.md) (ORDER BY ordinal).
+
+The column order is ignored in YQL by default:
+
+* The order of columns in the output tables and query results is undefined
+* The data scheme of the `UNION ALL` result is output by column names rather than positions
+
+If you enable `PRAGMA OrderedColumns;`, the order of columns is preserved in the query results and is derived from the order of columns in the input tables using the following rules:
+
+* `SELECT`: an explicit column enumeration dictates the result order.
+* `SELECT` with an asterisk (`SELECT * FROM ...`) inherits the order from its input.
+* The order of columns after [JOIN](../join.md): First output the left-hand columns, then the right-hand ones. If the column order in any of the sides in the `JOIN` output is undefined, the column order in the result is also undefined.
+* The order in `UNION ALL` depends on the [UNION ALL](union.md#union-all) execution mode.
+* The column order for [AS_TABLE](from_as_table.md) is undefined.
+
+
+### Combining queries {#combining-queries}
+
+Results of several SELECT statements (or subqueries) can be combined using `UNION` and `UNION ALL` keywords.
+
+```yql
+query1 UNION [ALL] query2 (UNION [ALL] query3 ...)
+```
+
+Union of more than two queries is interpreted as a left-associative operation, that is
+
+```yql
+query1 UNION query2 UNION ALL query3
+```
+
+is interpreted as
+
+```yql
+(query1 UNION query2) UNION ALL query3
+```
+
+If the underlying queries have one of the `ORDER BY/LIMIT/DISCARD/INTO RESULT` operators, the following rules apply:
+
+* `ORDER BY/LIMIT/INTO RESULT` is only allowed after the last query
+* `DISCARD` is only allowed before the first query
+* the operators apply to the `UNION [ALL]` as a whole, instead of referring to one of the queries
+* to apply the operator to one of the queries, enclose the query in parantheses
+
+## Clauses supported in SELECT
+
+* [FROM](from.md)
+* [FROM AS_TABLE](from_as_table.md)
+* [FROM SELECT](from_select.md)
+* [DISTINCT](distinct.md)
+* [UNIQUE DISTINCT](unique_distinct_hints.md)
+* [UNION](union.md)
+* [WITH](with.md)
+* [WITHOUT](without.md)
+* [WHERE](where.md)
+* [ORDER BY](order_by.md)
+* [ASSUME ORDER BY](assume_order_by.md)
+* [LIMIT OFFSET](limit_offset.md)
+* [SAMPLE](sample.md)
+* [TABLESAMPLE](sample.md)
+* [CONCAT](concat.md)
diff --git a/yql/essentials/docs/en/syntax/select/limit_offset.md b/yql/essentials/docs/en/syntax/select/limit_offset.md
new file mode 100644
index 00000000000..45e5bd1866e
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/limit_offset.md
@@ -0,0 +1,22 @@
+# LIMIT and OFFSET
+
+`LIMIT`: limits the output to the specified number of rows. By default, the output is not restricted.
+
+`OFFSET`: specifies the offset from the beginning (in rows). By default, it's zero.
+
+## Examples
+
+```yql
+SELECT key FROM my_table
+LIMIT 7;
+```
+
+```yql
+SELECT key FROM my_table
+LIMIT 7 OFFSET 3;
+```
+
+```yql
+SELECT key FROM my_table
+LIMIT 3, 7; -- equivalent to the previous example
+```
diff --git a/yql/essentials/docs/en/syntax/select/order_by.md b/yql/essentials/docs/en/syntax/select/order_by.md
new file mode 100644
index 00000000000..e33deeecc0d
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/order_by.md
@@ -0,0 +1,20 @@
+# ORDER BY
+
+Sorting the `SELECT` result using a comma-separated list of sorting criteria. As a criteria, you can use a column value or an expression on columns. Ordering by column sequence number is not supported (`ORDER BY N` where `N` is a number).
+
+Each criteria can be followed by the sorting direction:
+
+- `ASC`: Sorting in the ascending order. Applied by default.
+- `DESC`: Sorting in the descending order.
+
+Multiple sorting criteria will be applied left-to-right.
+
+## Example
+
+```yql
+SELECT key, string_column
+FROM my_table
+ORDER BY key DESC, LENGTH(string_column) ASC;
+```
+
+You can also use `ORDER BY` for [window functions](../window.md).
diff --git a/yql/essentials/docs/en/syntax/select/sample.md b/yql/essentials/docs/en/syntax/select/sample.md
new file mode 100644
index 00000000000..efe493fa18b
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/sample.md
@@ -0,0 +1,38 @@
+# TABLESAMPLE and SAMPLE
+
+Building a random sample from the data source specified in `FROM`.
+
+`TABLESAMPLE` is part of the SQL standard and works as follows:
+
+* The operating mode is specified:
+
+ * `BERNOULLI` means "slowly, straightforwardly going through all the data, but in a truly random way".
+ * `SYSTEM` uses knowledge about the physical data storage of data to avoid full data scans, but somewhat sacrificing randomness of the sample.
+
+The data is split into sufficiently large blocks, and the whole data blocks are sampled. For applied calculations on sufficiently large tables, the result may well be consistent.
+
+* The size of the random sample is indicated as a percentage after the operating mode, in parentheses.
+* Optionally, it can be followed by the `REPEATABLE` keyword and an integer in parentheses to be used as a seed for a pseudorandom number generator.
+
+`SAMPLE` is a shorter alias without sophisticated settings and sample size specified as a fraction. It currently corresponds to the `BERNOULLI` mode.
+
+## Examples
+
+```yql
+SELECT *
+FROM my_table
+TABLESAMPLE BERNOULLI(1.0) REPEATABLE(123); -- one percent of the table
+```
+
+```yql
+SELECT *
+FROM my_table
+TABLESAMPLE SYSTEM(1.0); -- about one percent of the table
+```
+
+```yql
+SELECT *
+FROM my_table
+SAMPLE 1.0 / 3; -- one-third of the table
+```
+
diff --git a/yql/essentials/docs/en/syntax/select/toc_i.yaml b/yql/essentials/docs/en/syntax/select/toc_i.yaml
new file mode 100644
index 00000000000..0c63de3ff24
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/toc_i.yaml
@@ -0,0 +1,17 @@
+items:
+- { name: Overview, href: index.md }
+- { name: FROM, href: from.md }
+- { name: FROM AS_TABLE, href: from_as_table.md }
+- { name: FROM SELECT, href: from_select.md }
+- { name: DISTINCT, href: distinct.md }
+- { name: UNIQUE DISTINCT, href: unique_distinct_hints.md }
+- { name: UNION, href: union.md }
+- { name: CONCAT, href: concat.md }
+- { name: WITH, href: with.md }
+- { name: WITHOUT, href: without.md }
+- { name: WHERE, href: where.md }
+- { name: ORDER BY, href: order_by.md }
+- { name: ASSUME ORDER BY, href: assume_order_by.md }
+- { name: LIMIT OFFSET, href: limit_offset.md }
+- { name: SAMPLE, href: sample.md }
+- { name: TABLESAMPLE, href: sample.md }
diff --git a/yql/essentials/docs/en/syntax/select/toc_p.yaml b/yql/essentials/docs/en/syntax/select/toc_p.yaml
new file mode 100644
index 00000000000..5bfec4365de
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/toc_p.yaml
@@ -0,0 +1,2 @@
+items:
+- include: { mode: link, path: toc_i.yaml } \ No newline at end of file
diff --git a/yql/essentials/docs/en/syntax/select/union.md b/yql/essentials/docs/en/syntax/select/union.md
new file mode 100644
index 00000000000..32b5ee18759
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/union.md
@@ -0,0 +1,63 @@
+# Combining subquery results (UNION)
+
+## UNION {#union}
+
+Union of the results of the underlying queries, with duplicates removed.
+Behavior is identical to using `UNION ALL` followed by `SELECT DISTINCT *`.
+Refer to [UNION ALL](#union-all) for more details.
+
+### Examples
+
+```yql
+SELECT key FROM T1
+UNION
+SELECT key FROM T2 -- returns the set of distinct keys in the tables
+```
+
+
+## UNION ALL {#union-all}
+
+Concatenating results of multiple `SELECT` statements (or subqueries).
+
+Two `UNION ALL` modes are supported: by column names (the default mode) and by column positions (corresponds to the ANSI SQL standard and is enabled by the [PRAGMA](../pragma.md#positionalunionall)).
+
+In the "by name" mode, the output of the resulting data schema uses the following rules:
+
+* The resulting table includes all columns that were found in at least one of the input tables.
+* If a column wasn't present in all the input tables, then it's automatically assigned the [optional data type](../../types/optional.md) (that can accept `NULL`).
+* If a column in different input tables had different types, then the shared type (the broadest one) is output.
+* If a column in different input tables had a heterogeneous type, for example, string and numeric, an error is raised.
+
+
+The order of output columns in this mode is equal to the largest common prefix of the order of inputs, followed by all other columns in the alphabetic order.
+If the largest common prefix is empty (for example, if the order isn't specified for one of the inputs), then the output order is undefined.
+
+In the "by position" mode, the output of the resulting data schema uses the following rules:
+
+* All inputs must have equal number of columns
+* The order of columns must be defined for all inputs
+* The names of the resulting columns must match the names of columns in the first table
+* The type of the resulting columns is output as a common (widest) type of input column types having the same positions
+
+The order of the output columns in this mode is the same as the order of columns in the first input.
+
+### Examples
+
+```yql
+SELECT 1 AS x
+UNION ALL
+SELECT 2 AS y
+UNION ALL
+SELECT 3 AS z;
+```
+
+In the default mode, this query returns a selection with three columns x, y, and z. When `PRAGMA PositionalUnionAll;` is enabled, the selection only includes the x column.
+
+```yql
+PRAGMA PositionalUnionAll;
+
+SELECT 1 AS x, 2 as y
+UNION ALL
+SELECT * FROM AS_TABLE([<|x:3, y:4|>]); -- error: the order of columns in AS_TABLE is undefined
+```
+
diff --git a/yql/essentials/docs/en/syntax/select/unique_distinct_hints.md b/yql/essentials/docs/en/syntax/select/unique_distinct_hints.md
new file mode 100644
index 00000000000..1e0f310aa1b
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/unique_distinct_hints.md
@@ -0,0 +1,10 @@
+# UNIQUE DISTINCT hints
+
+Directly after `SELECT`, it is possible to add [SQL hints](../lexer.md#sql-hints) `unique` or `distinct`, which declare that this projection generates data containing unique values in the specified set of columns of a row-based or columnar table. This can be used to optimize subsequent subqueries executed on this projection, or for writing to table meta-attributes during `INSERT`.
+
+* Columns are specified in the hint values, separated by spaces.
+* If the set of columns is not specified, uniqueness applies to the entire set of columns in this projection.
+* `unique` - indicates unique or `null` values. According to the SQL standard, each null is unique: `NULL = NULL` -> `NULL`.
+* `distinct` - indicates completely unique values including null: `NULL IS DISTINCT FROM NULL` -> `FALSE`.
+* Multiple sets of columns can be specified in several hints for a single projection.
+* If the hint contains a column that is not in the projection, it will be ignored.
diff --git a/yql/essentials/docs/en/syntax/select/where.md b/yql/essentials/docs/en/syntax/select/where.md
new file mode 100644
index 00000000000..e522ab82d6c
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/where.md
@@ -0,0 +1,11 @@
+# WHERE
+
+Filtering rows in the `SELECT` result based on a condition in tables.
+
+## Example
+
+```yql
+SELECT key FROM my_table
+WHERE value > 0;
+```
+
diff --git a/yql/essentials/docs/en/syntax/select/with.md b/yql/essentials/docs/en/syntax/select/with.md
new file mode 100644
index 00000000000..861dc1a7be7
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/with.md
@@ -0,0 +1,32 @@
+# WITH
+
+It's set after the data source in `FROM` and is used for additional hints for tables. You can't use hints for subqueries and [named expressions](../expressions.md#named-nodes).
+
+The following values are supported:
+
+* `INLINE`: Hints that the table contents is small and you need to use its in-memory view to process the query. The actual size of the table is not controlled in this case, and if it's large, the query might fail with an out-of-memory error.
+* `UNORDERED`: Suppresses original table sorting.
+* `SCHEMA` type: Hints that the specified table schema must be used entirely, ignoring the schema in the metadata.
+* `COLUMNS` type: Hints that the specified types should be used for columns whose names match the table's column names in the metadata, as well as which columns are additionally present in the table.
+
+When setting the `SCHEMA` and `COLUMNS` hints, the type must be a [structure](../../types/containers.md).
+
+If you use the `SCHEMA` hint, then with the table functions like [EACH](concat.md#each) you can use an empty list of tables that is treated as an empty table with columns defined in the `SCHEMA`.
+
+## Examples
+
+```yql
+SELECT key FROM my_table WITH INLINE;
+```
+
+```yql
+SELECT key, value FROM my_table WITH SCHEMA Struct<key:String, value:Int32>;
+```
+
+```yql
+SELECT key, value FROM my_table WITH COLUMNS Struct<value:Int32?>;
+```
+
+```yql
+SELECT key, value FROM EACH($my_tables) WITH SCHEMA Struct<key:String, value:List<Int32>>;
+```
diff --git a/yql/essentials/docs/en/syntax/select/without.md b/yql/essentials/docs/en/syntax/select/without.md
new file mode 100644
index 00000000000..7f85fab3bc2
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/select/without.md
@@ -0,0 +1,16 @@
+# WITHOUT
+
+Excluding columns from the result of `SELECT *`.
+
+## Examples
+
+```yql
+SELECT * WITHOUT foo, bar FROM my_table;
+```
+
+```yql
+PRAGMA simplecolumns;
+SELECT * WITHOUT t.foo FROM my_table AS t
+CROSS JOIN (SELECT 1 AS foo) AS v;
+```
+
diff --git a/yql/essentials/docs/en/syntax/subquery.md b/yql/essentials/docs/en/syntax/subquery.md
new file mode 100644
index 00000000000..10767ed07d3
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/subquery.md
@@ -0,0 +1,190 @@
+# Subquery templates
+
+## DEFINE SUBQUERY {#define-subquery}
+
+`DEFINE SUBQUERY` lets you declare a subquery template that is a parameterizable block of several top-level expressions (statements), and then use it repeatedly in the `FROM` section of [SELECT](select/index.md) statements or as input data in [PROCESS](process.md)/[REDUCE](reduce.md) with parameters.
+As opposed to [actions](action.md), the subquery template must end with the `SELECT`/`PROCESS`/`REDUCE` statement whose result is the subquery's return value. Keep in mind that you can't use the top-level `SELECT`/`PROCESS`/`REDUCE` statement and the modifying expressions (for example, `INSERT`) more than once.
+
+After `DEFINE SUBQUERY`, specify:
+
+1. [A named expression](expressions.md#named-nodes) that will be used to access the declared template further in the query.
+2. The round brackets contain a list of named expressions you can use to access parameters inside the subquery template.
+3. `AS` keyword.
+4. The list of top-level expressions.
+5. `END DEFINE` acts as a marker of the last expression inside the subquery template.
+
+One or more of the last subquery parameters can be marked with a question as optional: unless they haven't been specified when calling subquery, they will be assigned the `NULL` value.
+
+{% note info %}
+
+In large queries, you can use separate files for action definition and include them to the main query using [EXPORT](export_import.md#export) + [IMPORT](export_import.md#import) so that instead of one long text you can have several logical parts that are easier to navigate. An important nuance: the `USE my_cluster;` directive in the import query doesn't affect behavior of actions declared in other subquery files.
+
+{% endnote %}
+
+Even if the list of parameters in the subquery template definition is empty, when using it in `FROM`, specify the parentheses `()`. This may help to limit the scope of named expressions used in only one subquery.
+
+In some cases, instead of `DEFINE SUBQUERY` it's more convenient to use an equivalent [lambda function](expressions.md#lambda).
+In this case, the lambda function must accept, as the first argument, the special object called `world` that passes dependencies to make certain PRAGMA or COMMIT statements visible at the query template's point of use. Also, make sure to pass this object as the first argument along with the other arguments (if any) to other query templates, if you use them in your lambda function.
+The return value of the lambda function must have the structure list type (output table) or a list of variants over a tuple of structures (multiple output tables). In the latter case, the following unpacking is usually used at the query template's point of use:
+
+```yql
+$out1, $out2 = PROCESS $mySubquery($myParam1, $myParam2);
+-- next we use $out1 and $out2 as separate tables.
+```
+
+### Examples
+
+```yql
+DEFINE SUBQUERY $hello_world($name, $suffix?) AS
+ $name = $name ?? ($suffix ?? "world");
+ SELECT "Hello, " || $name || "!";
+END DEFINE;
+
+SELECT * FROM $hello_world(NULL); -- Hello, world!
+SELECT * FROM $hello_world("John"); -- Hello, John!
+SELECT * FROM $hello_world(NULL, "Earth"); -- Hello, Earth!
+```
+
+```yql
+DEFINE SUBQUERY $dup($x) AS
+ SELECT * FROM $x(1) -- apply the passed query template with one argument
+ UNION ALL
+ SELECT * FROM $x(2); -- ... and with other argument
+END DEFINE;
+
+DEFINE SUBQUERY $sub($n) AS
+ SELECT $n * 10;
+END DEFINE;
+
+SELECT * FROM $dup($sub); -- pass the query template $sub as a parameter
+-- Result:
+-- 10
+-- 20
+```
+
+```yql
+/* Hide the named expressions $a and $b inside a separate scope */
+DEFINE SUBQUERY $clean() AS
+ $a = 10;
+ $b = $a * $a;
+ SELECT $a AS a, $b AS b;
+END DEFINE;
+
+SELECT * FROM $clean(); -- a: 10, b: 100
+```
+
+```yql
+USE my_cluster;
+
+DEFINE SUBQUERY $input() as
+ SELECT * FROM `home/yql/tutorial/users`;
+END DEFINE;
+
+DEFINE SUBQUERY $myProcess1($nestedQuery, $lambda) AS
+ PROCESS $nestedQuery() -- the parentheses () are mandatory here
+ USING $lambda(TableRow());
+END DEFINE;
+
+$myProcess2 = ($world, $nestedQuery, $lambda) -> {
+ -- If you use ListFlatMap or YQL::OrderedFlatMap, you get an Ordered YT Map operation
+ return YQL::FlatMap($nestedQuery($world), $lambda);
+};
+
+-- With such use, the implementations of $myProcess1 and $myProcess2 are identical
+SELECT * FROM $myProcess1($input, ($x) -> { RETURN AsList($x, $x) });
+SELECT * FROM $myProcess2($input, ($x) -> { RETURN AsList($x, $x) });
+```
+
+```yql
+USE my_cluster;
+
+DEFINE SUBQUERY $runPartition($table) AS
+ $paritionByAge = ($row) -> {
+ $recordType = TypeOf($row);
+ $varType = VariantType(TupleType($recordType, $recordType));
+ RETURN If($row.age % 2 == 0,
+ Variant($row, "0", $varType),
+ Variant($row, "1", $varType),
+ );
+ };
+
+ PROCESS $table USING $paritionByAge(TableRow());
+END DEFINE;
+
+-- Unpacking two results
+$i, $j = (PROCESS $runPartition("home/yql/tutorial/users"));
+
+SELECT * FROM $i;
+
+SELECT * FROM $j;
+```
+
+## Combining the subquery templates: SubqueryExtend, SubqueryUnionAll, SubqueryMerge, SubqueryUnionMerge {#subquery-extend} {#subquery-unionall} {#subquery-merge} {#subquery-unionmerge}
+
+These functions combine the results of one or more subquery templates passed by arguments. The number of parameters in such subquery templates must be the same.
+
+* `SubqueryExtend` requires matching of subquery schemas.
+* `SubqueryUnionAll` follows the same rules as [ListUnionAll](../builtins/list.md#ListUnionAll).
+* `SubqueryMerge` uses the same constraints as `SubqueryExtend` and also outputs a sorted result if all subqueries have the same sort order.
+* `SubqueryUnionMerge` uses the same constraints as `SubqueryUnionAll` and also outputs a sorted result if all subqueries have the same sort order.
+
+### Examples
+
+```yql
+DEFINE SUBQUERY $sub1() as
+ SELECT 1 as x;
+END DEFINE;
+
+DEFINE SUBQUERY $sub2() as
+ SELECT 2 as x;
+END DEFINE;
+
+$s = SubqueryExtend($sub1,$sub2);
+PROCESS $s();
+```
+
+## Combining subquery templates after substituting list items: SubqueryExtendFor, SubqueryUnionAllFor, SubqueryMergeFor, SubqueryUnionMergeFor {#subquery-extend-for} {#subquery-unionall-for} {#subquery-merge-for} {#subquery-unionmerge-for}
+
+The functions take the following arguments:
+
+* A non-empty list of values.
+* A subquery template that must have exactly one parameter.
+
+They substitute each item from the list into the subquery template as a parameter and then combine the obtained subqueries.
+
+* `SubqueryExtendFor` requires matching of subquery schemas.
+* `SubqueryUnionAllFor` follows the same rules as [ListUnionAll](../builtins/list.md#ListUnionAll).
+* `SubqueryMergeFor` uses the same constraints as `SubqueryExtendFor` and also outputs a sorted result if all subqueries have the same sort order.
+* `SubqueryUnionMergeFor` uses the same constraints as `SubqueryUnionAllFor` and also outputs a sorted result if all subqueries have the same sort order.
+
+### Examples
+
+```yql
+DEFINE SUBQUERY $sub($i) as
+ SELECT $i as x;
+END DEFINE;
+
+$s = SubqueryExtendFor([1,2,3],$sub);
+PROCESS $s();
+```
+
+## Adding sorting to the SubqueryOrderBy template or indicating the presence of this SubqueryAssumeOrderBy
+
+The functions take the following arguments:
+
+* A subquery template without parameters.
+* A list of pairs (string indicating the column name and Boolean value: True for sorting in ascending order or False for sorting in descending order).
+
+And they build a new query template without parameters where sorting is performed or a comment on the use of sorting is added to the result. To use the resulting query template, call the `PROCESS` function, since, when using a `SELECT`, sorting is ignored.
+
+### Examples
+
+```yql
+DEFINE SUBQUERY $sub() as
+ SELECT * FROM (VALUES (1,'c'), (1,'a'), (3,'b')) AS a(x,y);
+end define;
+
+$sub2 = SubqueryOrderBy($sub, [('x',false), ('y',true)]);
+
+PROCESS $sub2();
+```
diff --git a/yql/essentials/docs/en/syntax/toc_i.yaml b/yql/essentials/docs/en/syntax/toc_i.yaml
new file mode 100644
index 00000000000..f91c0f3e1eb
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/toc_i.yaml
@@ -0,0 +1,23 @@
+items:
+- { name: Overview, href: index.md }
+- { name: Lexical structure, href: lexer.md }
+- { name: Expressions, href: expressions.md }
+- { name: ACTION, href: action.md }
+- { name: COMMIT, href: commit.md }
+- { name: DECLARE, href: declare.md }
+- { name: DISCARD, href: discard.md }
+- { name: GROUP BY, href: group_by.md }
+- { name: EXPORT and IMPORT, href: export_import.md }
+- { name: FLATTEN, href: flatten.md }
+- { name: INSERT, href: insert_into.md }
+- { name: INTO RESULT, href: into_result.md }
+- { name: JOIN, href: join.md }
+- { name: PRAGMA, href: pragma.md }
+- { name: PROCESS, href: process.md }
+- { name: REDUCE, href: reduce.md }
+- { name: SELECT, include: { mode: link, path: select/toc_p.yaml } }
+- { name: SUBQUERY, href: subquery.md }
+- { name: USE, href: use.md }
+- { name: VALUES, href: values.md }
+- { name: WINDOW, href: window.md }
+- { name: Unsupported statements, href: not_yet_supported.md }
diff --git a/yql/essentials/docs/en/syntax/use.md b/yql/essentials/docs/en/syntax/use.md
new file mode 100644
index 00000000000..913c36744f0
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/use.md
@@ -0,0 +1,24 @@
+# USE
+
+Specifying the "database". As a rule, one of the backend clusters is used as a database. This database will be used by default to search for tables whenever the database hasn't been specified explicitly.
+
+If the query doesn't include `USE`, then the cluster must be specified at the beginning of the table path in the format ``` cluster.`path/to/table` ```, for example ```cluster.`home/yql/test` ```. Backticks are used to automatically escape special characters (in this case, slashes).
+
+Usually the cluster name is specified explicitly, but you can use an expression for it. For example, this will let you use the parameters declared by [DECLARE](declare.md).
+In this case, `USE` must have the notation `USE yt:$cluster_name`, where `$cluster_name` is the [named expression](expressions.md#named-nodes) of the `String` type.
+Alternatively, you can specify the cluster right at the beginning of the table path in the format ```yt:$cluster_name.`path/to/table` ```.
+
+As far as `USE` is concerned, you can add it inside [actions](action.md) or [subquery templates](subquery.md). The value of the current cluster is inherited by declarations of nested actions or subqueries. The scope of `USE` is terminated at the end of the action or subquery template where it has been declared.
+
+## Examples
+
+```yql
+USE my_cluster;
+```
+
+{% note info %}
+
+`USE` **doesn't** guarantee that the query will be executed against the specified cluster. The query might be executed against other cluster if it doesn't use any input data (for example, `USE foo; SELECT 2 + 2;` ) or if the full path to the table on other cluster has been specified (for example, `USE foo; SELECT * FROM bar.``path/to/table``;`).
+
+{% endnote %}
+
diff --git a/yql/essentials/docs/en/syntax/values.md b/yql/essentials/docs/en/syntax/values.md
new file mode 100644
index 00000000000..c39805312cc
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/values.md
@@ -0,0 +1,45 @@
+# Basic VALUES syntax in YQL
+
+## VALUES as a top-level operator
+
+It lets you create a table from specified values. For example, this statement creates a table of k columns and n rows:
+
+```yql
+VALUES (expr_11, expr_12, ..., expr_1k),
+ (expr_21, expr_22, ..., expr_2k),
+ ....
+ (expr_n1, expr_n2, ..., expr_nk);
+```
+
+This statement is totally equivalent to the following one:
+
+```yql
+SELECT expr_11, expr_12, ..., expr_1k UNION ALL
+SELECT expr_21, expr_22, ..., expr_2k UNION ALL
+....
+SELECT expr_n1, expr_n2, ..., expr_nk;
+```
+
+### Example
+
+```yql
+VALUES (1,2), (3,4);
+```
+
+## VALUES after FROM
+
+VALUES can also be used in a subquery, after FROM. For example, the following two queries are equivalent:
+
+```yql
+VALUES (1,2), (3,4);
+SELECT * FROM (VALUES (1,2), (3,4));
+```
+
+In all the examples above, column names are assigned by YQL and have the format `column0 ... columnN`. To assign arbitrary column names, you can use the following construct:
+
+```yql
+SELECT * FROM (VALUES (1,2), (3,4)) as t(x,y);
+```
+
+In this case, the columns will get the names `x`, `y`.
+
diff --git a/yql/essentials/docs/en/syntax/window.md b/yql/essentials/docs/en/syntax/window.md
new file mode 100644
index 00000000000..7c7d9e7df8e
--- /dev/null
+++ b/yql/essentials/docs/en/syntax/window.md
@@ -0,0 +1,153 @@
+# OVER, PARTITION BY, and WINDOW
+
+Window functions were introduced in the SQL:2003 standard and expanded in the SQL:2011 standard. They let you run calculations on a set of table rows that are related to the current row in some way.
+
+Unlike [aggregate functions](../builtins/aggregation.md), window functions don't group rows into one output row: the number of rows in the resulting table is always the same as in the source table.
+
+If a query contains both aggregate and window functions, grouping is performed and aggregate function values are calculated first. The calculated values of aggregate functions can be used as window function arguments (but not the other way around).
+
+## Syntax {#syntax}
+
+General syntax for calling a window function is as follows
+
+```yql
+function_name([expression [, expression ...]]) OVER (window_definition)
+```
+
+or
+
+```yql
+function_name([expression [, expression ...]]) OVER window_name
+```
+
+Here, window name (`window_name`) is an arbitrary ID that is unique within the query and `expression` is an arbitrary expression that contains no window function calls.
+
+In the query, each window name must be mapped to the window definition (`window_definition`):
+
+```yql
+SELECT
+ F0(...) OVER (window_definition_0),
+ F1(...) OVER w1,
+ F2(...) OVER w2,
+ ...
+FROM my_table
+WINDOW
+ w1 AS (window_definition_1),
+ ...
+ w2 AS (window_definition_2)
+;
+```
+
+Here, the `window_definition` is written as
+
+```yql
+[ PARTITION BY (expression AS column_identifier | column_identifier) [, ...] ]
+[ ORDER BY expression [ASC | DESC] ]
+[ frame_definition ]
+```
+
+You can set an optional *frame definition* (`frame_definition`) one of two ways:
+
+* `ROWS frame_begin`
+* `ROWS BETWEEN frame_begin AND frame_end`
+
+*The frame start* (`frame_begin`) and *frame end* (`frame_end`) are set one of the following ways:
+
+* `UNBOUNDED PRECEDING`
+* `offset PRECEDING`
+* `CURRENT ROW`
+* `offset FOLLOWING`
+* `UNBOUNDED FOLLOWING`
+
+Here, the *frame* `offset` is a non-negative numeric literal. If the frame end isn't set, the `CURRENT ROW` is assumed.
+
+There should be no window function calls in any of the expressions inside the window definition.
+
+## Calculation algorithm
+
+### Partitioning {#partition}
+
+If `PARTITION BY` is set, the source table rows are grouped into *partitions*, which are then handled independently of each other.
+If `PARTITION BY` isn't set, all rows in the source table are put in the same partition. If `ORDER BY` is set, it determines the order of rows in a partition.
+Both in `PARTITION BY` and [GROUP BY](group_by.md) you can use aliases and [SessionWindow](group_by.md#session-window).
+
+If `ORDER BY` is omitted, the order of rows in the partition is undefined.
+
+### Frame {#frame}
+
+The `frame_definition` specifies a set of partition rows that fall into the *window frame* associated with the current row.
+
+In `ROWS` mode (the only one that YQL currently supports), the window frame contains rows with the specified offsets relative to the current row in the partition. For example, if `ROWS BETWEEN 3 PRECEDING AND 5 FOLLOWING` is used, the window frame contains 3 rows preceding the current one, the current row, and 5 rows following it.
+
+The set of rows in the window frame may change depending on which row is the current one. For example, for the first row in the partition, the `ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING` window frame will have no rows.
+
+Setting `UNBOUNDED PRECEDING` as the frame start means "from the first partition row" and `UNBOUNDED FOLLOWING` as the frame end — "up to the last partition row". Setting `CURRENT ROW` means "from/to the current row".
+
+If no `frame_definition` is specified, a set of rows to be included in the window frame depends on whether there is `ORDER BY` in the `window_definition`.
+Namely, if there is `ORDER BY`, then `ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW` is implicitly assumed. If none, then `ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING`.
+
+Further, depending on the specific window function, it's calculated either based on the set of rows in the partition or the set of rows in the window frame.
+
+[List of available window functions](../builtins/window.md)
+
+#### Examples
+
+```yql
+SELECT
+ COUNT(*) OVER w AS rows_count_in_window,
+ some_other_value -- access the current row
+FROM `my_table`
+WINDOW w AS (
+ PARTITION BY partition_key_column
+ ORDER BY int_column
+);
+```
+
+```yql
+SELECT
+ LAG(my_column, 2) OVER w AS row_before_previous_one
+FROM `my_table`
+WINDOW w AS (
+ PARTITION BY partition_key_column
+);
+```
+
+```yql
+SELECT
+ -- AVG (like all aggregate functions used as window functions)
+ -- is calculated on the window frame
+ AVG(some_value) OVER w AS avg_of_prev_current_next,
+ some_other_value -- access the current row
+FROM my_table
+WINDOW w AS (
+ PARTITION BY partition_key_column
+ ORDER BY int_column
+ ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
+);
+```
+
+```yql
+SELECT
+ -- LAG doesn't depend on the window frame position
+ LAG(my_column, 2) OVER w AS row_before_previous_one
+FROM my_table
+WINDOW w AS (
+ PARTITION BY partition_key_column
+ ORDER BY my_column
+);
+```
+
+## Implementation specifics
+
+* Functions calculated on the `ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING` or `ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW` window frame are implemented efficiently (do not require additional memory and their computation runs on a partition in O(partition size) time).
+* For the `ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING` window frame, you can choose the execution strategy in RAM by specifying the `COMPACT` hint after the `PARTITION` keyword.
+
+ For example, `PARTITION COMPACT BY key` or `PARTITION COMPACT BY ()` (if `PARTITION BY` was missing initially).
+
+ If the `COMPACT` hint is specified, this requires additional memory equal to O(partition size), but then no extra `JOIN` operation is made.
+
+* If the window frame doesn't start with `UNBOUNDED PRECEDING`, calculating window functions on this window requires additional memory equal to O(the maximum number of rows from the window boundaries to the current row), while the computation time is equal to O(number_of_partition_rows * window_size).
+* For the window frame starting with `UNBOUNDED PRECEDING` and ending with `N`, where `N` is neither `CURRENT ROW` nor `UNBOUNDED FOLLOWING`, additional memory equal to O(N) is required and the computation time is equal to O(N * number_of_partition_rows).
+* The `LEAD(expr, N)` and `LAG(expr, N)` functions always require O(N) of RAM.
+
+Given the above, a query with `ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING` should, if possible, be changed to `ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW` by reversing the `ORDER BY` sorting order.
diff --git a/yql/essentials/docs/en/toc.yaml b/yql/essentials/docs/en/toc.yaml
new file mode 100644
index 00000000000..5bfec4365de
--- /dev/null
+++ b/yql/essentials/docs/en/toc.yaml
@@ -0,0 +1,2 @@
+items:
+- include: { mode: link, path: toc_i.yaml } \ No newline at end of file
diff --git a/yql/essentials/docs/en/toc_i.yaml b/yql/essentials/docs/en/toc_i.yaml
new file mode 100644
index 00000000000..7396a171419
--- /dev/null
+++ b/yql/essentials/docs/en/toc_i.yaml
@@ -0,0 +1,11 @@
+title: YQL core reference
+href: index.md
+items:
+- name: Data types
+ include: { mode: link, path: types/toc_i.yaml }
+- name: Syntax
+ include: { mode: link, path: syntax/toc_i.yaml }
+- name: Built-in functions
+ include: { mode: link, path: builtins/toc_i.yaml }
+- name: Recipes
+ include: { mode: link, path: recipes/toc_i.yaml }
diff --git a/yql/essentials/docs/en/types/cast.md b/yql/essentials/docs/en/types/cast.md
new file mode 100644
index 00000000000..6136c2f5802
--- /dev/null
+++ b/yql/essentials/docs/en/types/cast.md
@@ -0,0 +1,74 @@
+# Rules for type casting using the operator [CAST](../syntax/expressions.md#cast)
+
+## Rules for casting primitive data types
+
+* When casting primitive data types, some of the source information may be discarded unless contained in the target type. For example:
+
+ * The `Float`/`Double` fractional part, when casting to integer types.
+ * The `Datetime`/`Timestamp` time, when casting to `Date`.
+ * The timezone, when casting from timezone types to date/time types without a timezone.
+
+* If, in a certain combination of the source and target type, casting can't be performed for all possible values of the source type, then, if the casting fails, `CAST` returns `NULL`. In such cases, one `Optional` level is added to the return value type, unless already present. For example, the constructs: `CAST("3.14" AS Float?)` and `CAST("3.14" AS Float)` are fully equivalent and return `Float?`.
+* If casting is possible for all values of the source type, then adding '?' works the same way as `Just` on top: `CAST(3.14 AS Utf8?)` is same as `Just(CAST(3.14 AS Utf8))`.
+
+All combinations of primitive data types for which `CAST` can be used are described [here](primitive.md).
+
+## Casting rules for containers
+
+### Rules for Optional
+
+* If a higher `Optional` level is set for the target type than for the source type, it's same as adding `Just` on top of `CAST` with a lower `Optional` level.
+* If the source type has a higher level of `Optional` for the source type, then `NULL` at any level higher than the target level results in `NULL`.
+* At equal levels of `Optional`, the `NULL` value preserves the same level.
+
+```yql
+SELECT
+ CAST(1 AS Int32?), -- is equivalent to Just(1)
+ CAST(Just(2/1) AS Float??), -- [2]
+ CAST(Just(3/0) AS Float??) IS NULL; -- false: the result is Just(NULL)
+```
+
+### Rules for List/Dict
+
+* To create a list, `CAST` is applied to each item in the source list to cast it to the target type.
+* If the target item type is non-optional and `CAST` on the item might fail, then such casting is discarded. In this case, the resulting list might be shorter or even empty if every casting failed.
+* For dictionaries, the casting is totally similar to lists, with `CAST` being applied to keys and values.
+
+```yql
+SELECT
+ CAST([-1, 0, 1] AS List<Uint8?>), -- [null, 0, 1]
+ CAST(["3.14", "bad", "42"] AS List<Float>), -- [3.14, 42]
+
+ CAST({-1:3.14, 7:1.6} AS Dict<Uint8, Utf8>), -- {7: "1.6"}
+ CAST({-1:3.14, 7:1.6} AS Dict<Uint8?, Utf8>); -- {7: "1.6", null:"3.14"}
+```
+
+### Rules for Struct/Tuple
+
+* A structure or tuple is created by applying `CAST` to each item of the source type to cast it to an item with the same name or target type index.
+* If some field is missing in the target type, it's simply discarded.
+* If some field is missing in the source value type, then it can be added only if it's optional and accepts the `NULL` value.
+* If some field is non-optional in the target type, but its casting might fail, then `CAST` adds Optional to the structure or tuple level and might return `NULL` for the entire result.
+
+```yql
+SELECT
+ CAST((-1, 0, 1) AS Tuple<Uint16?, Uint16?, Utf8>), -- (null, 0, "1")
+ CAST((-2, 0) AS Tuple<Uint16, Utf8>), -- null
+ CAST((3, 4) AS Tuple<Uint16, String>), -- (3, "4"): the type is Tuple<Uint16, String>?
+ CAST(("4",) AS Tuple<Uint16, String?>), -- (4, null)
+ CAST((5, 6, null) AS Tuple<Uint8?>); -- (5,): the items were removed.
+
+SELECT -- One field was removed and one field was added: ("three":null, "two": "42")
+ CAST(<|one:"8912", two:42|> AS Struct<two:Utf8, three:Date?>);
+```
+
+### Rules for Variant
+
+* A variant with a specific name or index is cast to a variant with the same name or index.
+* If casting of a variant might fail and the type of this variant is non-optional, then `CAST` adds Optional to the top level and can return `NULL`.
+* If some variant is missing in the target type, then `CAST` adds Optional to the top level and returns `NULL` for such a value.
+
+### Nested containers
+
+* All of the above rules are applied recursively for nested containers.
+
diff --git a/yql/essentials/docs/en/types/containers.md b/yql/essentials/docs/en/types/containers.md
new file mode 100644
index 00000000000..523fa313132
--- /dev/null
+++ b/yql/essentials/docs/en/types/containers.md
@@ -0,0 +1,26 @@
+# Containers
+
+YQL supports container types to define complex data structures organized in various ways.
+Values of container types can be passed to YQL queries as input parameters or returned from YQL queries as columns of the set of results.
+
+| Type | Declaration,<br/>example | Description |
+| ------------ | ---------------- | ------------- |
+| List | `List<Type>`,<br/>`List<Int32>` | A variable-length sequence consisting of same-type elements. |
+| Dictionary | `Dict<KeyType, ValueType>`,<br/>`Dict<String,Int32>` | Set of key-value pairs with a fixed type of keys and values. |
+| Set | `Set<KeyType>`,<br/>`Set<String>` | A set of elements with a fixed type is a special case of a dictionary with the `Void` value type. |
+| Tuple | `Tuple<Type1, ..., TypeN>`,<br/>`Tuple<Int32,Double>` | Set of unnamed fixed-length elements with types specified for all elements. |
+| Structure | `Struct<Name1:Type1, ..., NameN:TypeN>`,<br/> `Struct<Name:String,Age:Int32>` | A set of named fields with specified value types, fixed at query start (must be data-independent). |
+| Stream | `Stream<Type>`,<br/> `Stream<Int32>` | Single-pass iterator by same-type values, not serializable |
+| Variant on tuple | `Variant<Type1, Type2>`,<br/> `Variant<Int32,String>` | A tuple known to have exactly one element filled |
+| Variant on structure | `Variant<Name1:Type1, Name2:Type2>`,<br/>`Variant<value:Int32,error:String>` | A structure known to have exactly one element filled |
+| Enumeration | `Enum<Name1, Name2>`,<br/>`Enum<value,error>` | A container with exactly one enumeration element selected and defined only by its name. |
+
+If necessary, you can nest containers in any combination, for example, `List<Tuple<Int32,Int32>>`.
+
+In certain contexts, [optional values](optional.md) can also be considered a container type (`Optional<Type>`) that behaves like a list of length 0 or 1.
+
+To create literals of list containers, dictionary containers, set containers, tuple containers, or structure containers, you can use the [operator notation](../builtins/basic.md#containerliteral).
+To create a variant literal over a tuple or structure, use the function [Variant](../builtins/basic.md#variant).
+To create an enumeration literal, use the function [Enum](../builtins/basic.md#enum).
+
+To access the container elements, use a [dot or square brackets](../syntax/expressions.md#items-access), depending on the container type.
diff --git a/yql/essentials/docs/en/types/index.md b/yql/essentials/docs/en/types/index.md
new file mode 100644
index 00000000000..f1c2fbab3b2
--- /dev/null
+++ b/yql/essentials/docs/en/types/index.md
@@ -0,0 +1,12 @@
+# YQL data types
+
+This section contains articles on YQL data types:
+
+- [Simple/Primitive types](primitive.md)
+- [Optional types](optional.md)
+- [Containers](containers.md)
+- [Special types](special.md)
+- [Type casting](cast.md)
+- [Text representation of data types](type_string.md)
+- [Data representation in JSON format](json.md)
+
diff --git a/yql/essentials/docs/en/types/json.md b/yql/essentials/docs/en/types/json.md
new file mode 100644
index 00000000000..115dc7c0d51
--- /dev/null
+++ b/yql/essentials/docs/en/types/json.md
@@ -0,0 +1,187 @@
+# Data representation in JSON format
+
+## Bool {#bool}
+
+Boolean value.
+
+* Type in JSON: `bool`.
+* Value example: `true`.
+* Sample JSON value: `true`.
+
+## Int8, Int16, Int32, Int64 {#int}
+
+Signed integer types.
+
+* Type in JSON: `number`.
+* Value example: `123456`, `-123456`.
+* Sample JSON value: `123456`, `-123456`.
+
+## Uint8, Uint16, Uint32, Uint64 {#uint}
+
+Unsigned integer types.
+
+* Type in JSON: `number`.
+* Value example: `123456`.
+* Sample JSON value: `123456`.
+
+## Float {#float}
+
+Real 4-byte number.
+
+* Type in JSON: `number`.
+* Value example: `0.12345679`.
+* Sample JSON value: `0.12345679`.
+
+## Double {#double}
+
+Real 8-byte number.
+
+* Type in JSON: `number`.
+* Value example: `0.12345678901234568`.
+* Sample JSON value: `0.12345678901234568`.
+
+## Decimal {#decimal}
+
+Fixed-precision number. Only Decimal(22, 9) is supported.
+
+* Type in JSON: `string`.
+* Value example: `-320.789`.
+* Sample JSON value: `"-320.789"`.
+
+## String, Yson {#string}
+
+Binary strings. Encoding algorithm depending on the byte value:
+
+* [0-31] — `\u00XX` (6 characters denoting the Unicode character code).
+* [32-126] — as is. These are readable single-byte characters that don't need to be escaped.
+* [127-255] — `\u00XX`.
+
+Decoding is a reverse process. Character codes in `\u00XX`, maximum 255.
+
+* Type in JSON: `string`.
+* Value example: A sequence of 4 bytes:
+
+ * 5 `0x05`: A control character.
+ * 10 `0x0a`: The `\n` newline character.
+ * 107 `0x6b`: The `k` character.
+ * 255 `0xff`: The `ÿ` character in Unicode.
+
+* Sample JSON value: `"\u0005\nk\u00FF"`.
+
+## Utf8, Json, Uuid {#utf}
+
+String types in UTF-8. Such strings are represented in JSON as strings with JSON characters escaped: `\\`, `\"`, `\n`, `\r`, `\t`, `\f`.
+
+* Type in JSON: `string`.
+
+* Value example: C++ code:
+
+ ```c++
+ "Escaped characters: "
+ "\\ \" \f \b \t \r\n"
+ "Non-escaped characters: "
+ "/ ' < > & []() ".
+ ```
+
+* Sample JSON value: `"Escaped characters: \\ \" \f \b \t \r\nNon-escaped characters: / ' < > & []() "`.
+
+## Date {#date}
+
+Date. Uint64, unix time days.
+
+* Type in JSON: `string`.
+* Value example: `18367`.
+* Sample JSON value: `"2020-04-15"`.
+
+## Datetime {#datetime}
+
+Date and time. Uint64, unix time seconds.
+
+* Type in JSON: `string`.
+* Value example: `1586966302`.
+* Sample JSON value: `"2020-04-15T15:58:22Z"`.
+
+## Timestamp {#timestamp}
+
+Date and time. Uint64, unix time microseconds.
+
+* Type in JSON: `string`.
+* Value example: `1586966302504185`.
+* Sample JSON value: `"2020-04-15T15:58:22.504185Z"`.
+
+## Interval {#interval}
+
+Time interval. Int64, precision to the microsecond, the interval values must not exceed 24 hours.
+
+* Type in JSON: `number`.
+* Value example: `123456`, `-123456`.
+* Sample JSON value: `123456`, `-123456`.
+
+## Optional {#optional}
+
+Means that the value can be `null`. If the value is `null`, then in JSON it's also `null`. If the value is not `null`, then the JSON value is expressed as if the type isn't `Optional`.
+
+* Type in JSON is missing.
+* Value example: `null`.
+* Sample JSON value: `null`.
+
+## List {#list}
+
+List. An ordered set of values of a given type.
+
+* Type in JSON: `array`.
+* Value example:
+
+ * Type: `List<Int32>`.
+ * Value: `1, 10, 100`.
+
+* Sample JSON value: `[1,10,100]`.
+
+## Stream {#stream}
+
+Stream. Single-pass iterator by same-type values,
+
+* Type in JSON: `array`.
+* Value example:
+
+ * Type: `Stream<Int32>`.
+ * Value: `1, 10, 100`.
+
+* Sample JSON value: `[1,10,100]`.
+
+## Struct {#struct}
+
+Structure. An unordered set of values with the specified names and type.
+
+* Type in JSON: `object`.
+* Value example:
+
+ * Type: `Struct<'Id':Uint32,'Name':String,'Value':Int32,'Description':Utf8?>`;
+ * Value: `"Id":1,"Name":"Anna","Value":-100,"Description":null`.
+
+* Sample JSON value: `{"Id":1,"Name":"Anna","Value":-100,"Description":null}`.
+
+## Tuple {#tuple}
+
+Tuple. An ordered set of values of the set types.
+
+* Type in JSON: `array`.
+* Value example:
+
+ * Type: `Tuple<Int32??,Int64???,String??,Utf8???>`;
+ * Value: `10,-1,null,"Some string"`.
+
+* Sample JSON value: `[10,-1,null,"Some string"]`.
+
+## Dict {#dict}
+
+Dictionary. An unordered set of key-value pairs. The type is set both for the key and the value. It's written in JSON to an array of arrays including two items.
+
+* Type in JSON: `array`.
+* Value example:
+
+ * Type: `Dict<Int64,String>`.
+ * Value: `1:"Value1",2:"Value2"`.
+
+* Sample JSON value: `[[1,"Value1"],[2,"Value2"]]`.
+
diff --git a/yql/essentials/docs/en/types/optional.md b/yql/essentials/docs/en/types/optional.md
new file mode 100644
index 00000000000..d1e1f69cefc
--- /dev/null
+++ b/yql/essentials/docs/en/types/optional.md
@@ -0,0 +1,50 @@
+## Data types accepting NULL
+
+Any typed data in YQL, including table columns, can be either non-nullable (guaranteed value) or nullable (empty value denoted as `NULL`). Data types that can include `NULL` values are called *optional* or, in SQL terms, *nullable*.
+
+Optional data types in the [text format](type_string.md) use the question mark at the end (for example, `String?`) or the notation `Optional<...>`.
+The following operations are most often performed on optional data types:
+
+* [IS NULL](../syntax/expressions.md#is-null): Matching an empty value
+* [COALESCE](../builtins/basic.md#coalesce): Leave the filled values unchanged and replace `NULL` with the default value that follows
+* [UNWRAP](../builtins/basic.md#optional-ops): Extract the value of the original type from the optional data type, `T?`. is converted to `T`
+* [JUST](../builtins/basic#optional-ops): Add optionality to the current type, `T` is converted to `T?`.
+* [NOTHING](../builtins/basic.md#optional-ops): Create an empty value with the specified type.
+
+`Optional` (nullable) isn't a property of a data type or column, but a container type where [containers](containers.md) can be arbitrarily nested into each other. For example, a column with the type `Optional<Optional<Boolean>>` can accept 4 values: `NULL` of the whole container, `NULL` of the inner container, `TRUE`, and `FALSE`. The above-declared type differs from `List<List<Boolean>>`, because it uses `NULL` as an empty list, and you can't put more than one non-null element in it. In addition, `Optional<Optional<T>>` type values are returned as results when searching by the key in the `Dict(k,v)` dictionary with `Optional<T>` type values. Using this type of result data, you can distinguish between a `NULL` value in the dictionary and a situation when the key is missing.
+
+### Example
+
+```yql
+$dict = {"a":1, "b":null};
+$found = $dict["b"];
+select if($found is not null, unwrap($found), -1);
+```
+
+Result:
+
+```text
+# column0
+null
+```
+
+## Logical and arithmetic operations with NULL {#null_expr}
+
+The `NULL` literal has a separate singular `Null` type and can be implicitly converted to any optional type (for example, the nested type `Optional<Optional<...Optional<T>...>>`). In ANSI SQL, `NULL` means "an unknown value", that's why logical and arithmetic operations involving `NULL` or empty `Optional` have certain specifics.
+
+### Examples
+
+```yql
+SELECT
+ True OR NULL, -- Just(True) (works the same way as True OR <unknown value of type Bool>)
+ False AND NULL, -- Just(False)
+ True AND NULL, -- NULL (more precise than Nothing<Bool?> – <unknown value of type Bool>)
+ NULL OR NOT NULL, -- NULL (all NULLs are "different")
+ 1 + NULL, -- NULL (Nothing<Int32?>) - the result of adding 1 together with
+ -- unknown value of type Int)
+ 1 == NULL, -- NULL (the result of adding 1 together with unknown value of type Int)
+ (1, NULL) == (1, 2), -- NULL (composite elements are compared by component
+ -- through `AND`)
+ (2, NULL) == (1, 3), -- Just(False) (expression is equivalent to 2 == 1 AND NULL == 3)
+
+```
diff --git a/yql/essentials/docs/en/types/primitive.md b/yql/essentials/docs/en/types/primitive.md
new file mode 100644
index 00000000000..24857a2f643
--- /dev/null
+++ b/yql/essentials/docs/en/types/primitive.md
@@ -0,0 +1,188 @@
+# Primitive data types
+
+<!-- markdownlint-disable blanks-around-fences -->
+
+The terms "simple", "primitive", and "elementary" data types are used synonymously.
+
+## Numeric types {#numeric}
+
+| Type | Description | Notes |
+| ----- | ----- | ----- |
+| `Bool` | Boolean value. |
+| `Int8` | A signed integer.<br/>Acceptable values: from -2<sup>7</sup> to 2<sup>7</sup>–1. | |
+| `Int16` | A signed integer.<br/>Acceptable values: from –2<sup>15</sup> to 2<sup>15</sup>–1. | |
+| `Int32` | A signed integer.<br/>Acceptable values: from –2<sup>31</sup> to 2<sup>31</sup>–1. | |
+| `Int64` | A signed integer.<br/>Acceptable values: from –2<sup>63</sup> to 2<sup>63</sup>–1. | |
+| `Uint8` | An unsigned integer.<br/>Acceptable values: from 0 to 2<sup>8</sup>–1. | |
+| `Uint16` | An unsigned integer.<br/>Acceptable values: from 0 to 2<sup>16</sup>–1. | |
+| `Uint32` | An unsigned integer.<br/>Acceptable values: from 0 to 2<sup>32</sup>–1. | |
+| `Uint64` | An unsigned integer.<br/>Acceptable values: from 0 to 2<sup>64</sup>–1. | |
+| `Float` | A real number with variable precision, 4 bytes in size. | |
+| `Double` | A real number with variable precision, 8 bytes in size. | |
+| `Decimal` | A real number with the specified precision, up to 35 decimal digits | |
+|`DyNumber` | A binary representation of a real number with an accuracy of up to 38 digits.<br/>Acceptable values: positive numbers from 1×10<sup>-130</sup> up to 1×10<sup>126</sup>–1, negative numbers from -1×10<sup>126</sup>–1 to -1×10<sup>-130</sup>, and 0.<br/>Compatible with the `Number` type in AWS DynamoDB. | |
+
+
+## String types {#string}
+
+| Type | Description | Notes |
+| ----- | ----- | ----- |
+| `String` | A string that can contain any binary data |
+| `Utf8` | Text encoded in [UTF-8](https://en.wikipedia.org/wiki/UTF-8) |
+| `Json` | [JSON](https://en.wikipedia.org/wiki/JSON) represented as text | Doesn't support matching |
+| `JsonDocument` | [JSON](https://en.wikipedia.org/wiki/JSON) in an indexed binary representation | Doesn't support matching |
+| `Yson` | [YSON](../udf/list/yson.md) in a textual or binary representation. | Doesn't support matching |
+| `Uuid` | Universally unique identifier [UUID](https://tools.ietf.org/html/rfc4122) |
+
+
+Unlike the `JSON` data type that stores the original text representation passed by the user, `JsonDocument` uses an indexed binary representation. An important difference from the point of view of semantics is that `JsonDocument` doesn't preserve formatting, the order of keys in objects, or their duplicates.
+
+Thanks to the indexed view, `JsonDocument` lets you bypass the document model using `JsonPath` without the need to parse the full content. This helps efficiently perform operations from the [JSON API](../builtins/json.md), reducing delays and cost of user queries. Execution of `JsonDocument` queries can be up to several times more efficient depending on the type of load.
+
+Due to the added redundancy, `JsonDocument` is less effective in storage. The additional storage overhead depends on the specific content, but is 20-30% of the original volume on average. Saving data in `JsonDocument` format requires additional conversion from the textual representation, which makes writing it less efficient. However, for most read-intensive scenarios that involve processing data from JSON, this data type is preferred and recommended.
+
+{% note warning %}
+
+To store numbers (JSON Number) in `JsonDocument`, as well as for arithmetic operations on them in the [JSON API](../builtins/json.md), the [Double](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) type is used. Precision might be lost when non-standard representations of numbers are used in the source JSON document.
+
+{% endnote %}
+
+
+
+## Date and time {#datetime}
+
+| Type | Description | Notes |
+| ----- | ----- | ----- |
+| `Date` | Date, precision to the day | Range of values for all time types except `Interval`: From 00:00 01.01.1970 to 00:00 01.01.2106. Internal `Date` representation: Unsigned 16-bit integer |
+| `Datetime` | Date/time, precision to the second | Internal representation: Unsigned 32-bit integer |
+| `Timestamp` | Date/time, precision to the microsecond | Internal representation: Unsigned 64-bit integer |
+| `Interval` | Time interval (signed), precision to microseconds | Value range: From -136 years to +136 years. Internal representation: Signed 64-bit integer. |
+| `TzDate` | Date with time zone label, precision to the day | Not supported in table columns |
+| `TzDateTime` | Date/time with time zone label, precision to the second | Not supported in table columns |
+| `TzTimestamp` | Date/time with time zone label, precision to the microsecond | Not supported in table columns |
+
+
+
+### Supporting types with a time zone label
+
+Time zone label for the `TzDate`, `TzDatetime`, `TzTimestamp` types is an attribute that is used:
+
+* When converting ([CAST](../syntax/expressions.md#cast), [DateTime::Parse](../udf/list/datetime.md#parse), [DateTime::Format](../udf/list/datetime.md#format)) to a string and from a string.
+* In [DateTime::Split](../udf/list/datetime.md#split), a timezone component is added to `Resource<TM>`.
+
+The point in time for these types is stored in UTC, and the timezone label doesn't participate in any other calculations in any way. For example:
+
+```yql
+SELECT -- these expressions are always true for any timezones: the timezone doesn't affect the point in time.
+ AddTimezone(CurrentUtcDate(), "Europe/Moscow") ==
+ AddTimezone(CurrentUtcDate(), "America/New_York"),
+ AddTimezone(CurrentUtcDatetime(), "Europe/Moscow") ==
+ AddTimezone(CurrentUtcDatetime(), "America/New_York");
+```
+
+Keep in mind that when converting between `TzDate` and `TzDatetime`, or `TzTimestamp` the date's midnight doesn't follow the local time zone, but midnight in UTC for the date in UTC.
+
+
+
+## Casting between data types {#cast}
+
+### Explicit casting {#explicit-cast}
+
+Explicit casting using [CAST](../syntax/expressions.md#cast):
+
+#### Casting to numeric types
+
+| Type | Bool | Int | Uint | Float | Double | Decimal |
+| --- | --- | --- | --- | --- | --- | --- |
+| **Bool** | — | Yes<sup>1</sup> | Yes<sup>1</sup> | Yes<sup>1</sup> | Yes<sup>1</sup> | No | Yes | No |
+| **INT** | Yes<sup>2</sup> | — | Yes<sup>3</sup> | Yes | Yes | Yes |
+| **Uint** | Yes<sup>2</sup> | Yes | — | Yes | Yes | Yes |
+| **Float** | Yes<sup>2</sup> | Yes | Yes | — | Yes | No |
+| **Double** | Yes<sup>2</sup> | Yes | Yes | Yes | — | No |
+| **Decimal** | No | Yes | Yes | Yes | Yes | — |
+| **String** | Yes | Yes | Yes | Yes | Yes | Yes |
+| **Utf8** | Yes | Yes | Yes | Yes | Yes | Yes |
+| **Json** | No | No | No | No | No | No |
+| **Yson** | Yes<sup>4</sup> | Yes<sup>4</sup> | Yes<sup>4</sup> | Yes<sup>4</sup> | Yes<sup>4</sup> | Yes<sup>4</sup> |
+| **Uuid** | No | No | No | No | No | No |
+| **Date** | No | Yes | Yes | Yes | Yes | No | Yes |
+| **Datetime** | No | Yes | Yes | Yes | Yes | No |
+| **Timestamp** | No | Yes | Yes | Yes | Yes | No |
+| **Interval** | No | Yes | Yes | Yes | Yes | No |
+
+<sup>1</sup> `True` is converted to `1` and `False` to `0`.
+<sup>2</sup> Any value other than `0` is converted to `True`, `0` is converted to `False`.
+<sup>3</sup> Possible only in the case of a non-negative value.
+<sup>4</sup> Using the built-in function [Yson::ConvertTo](../udf/list/yson.md#ysonconvertto).
+
+#### Converting to date and time data types
+
+| Type | Date | Datetime | Timestamp | Interval |
+| --- | --- | --- | --- | --- |
+| **Bool** | No | No | No | No |
+| **INT** | Yes | Yes | Yes | Yes |
+| **Uint** | Yes | Yes | Yes | Yes |
+| **Float** | No | No | No | No |
+| **Double** | No | No | No | No |
+| **Decimal** | No | No | No | No |
+| **String** | Yes | Yes | Yes | Yes |
+| **Utf8** | Yes | Yes | Yes | Yes |
+| **Json** | No | No | No | No |
+| **Yson** | No | No | No | No |
+| **Uuid** | No | No | No | No |
+| **Date** | — | Yes | Yes | No |
+| **Datetime** | Yes | — | Yes | No |
+| **Timestamp** | Yes | Yes | — | No |
+| **Interval** | No | No | No | — | — |
+
+#### Conversion to other data types
+
+| Type | String | Utf8 | Json | Yson | Uuid |
+| --- | --- | --- | --- | --- | --- |
+| **Bool** | Yes | No | No | No | No |
+| **INT** | Yes | No | No | No | No |
+| **Uint** | Yes | No | No | No | No |
+| **Float** | Yes | No | No | No | No |
+| **Double** | Yes | No | No | No | No |
+| **Decimal** | Yes | No | No | No | No |
+| **String** | — | Yes | Yes | Yes | Yes |
+| **Utf8** | Yes | — | No | No | No |
+| **Json** | Yes | Yes | — | No | No |
+| **Yson** | Yes<sup>4</sup> | No | No | No | No |
+| **Uuid** | Yes | Yes | No | No | — |
+| **Date** | Yes | Yes | No | No | No |
+| **Datetime** | Yes | Yes | No | No | No |
+| **Timestamp** | Yes | Yes | No | No | No |
+| **Interval** | Yes | Yes | No | No | No |
+
+<sup>4</sup> Using the built-in function [Yson::ConvertTo](../udf/list/yson.md#ysonconvertto).
+
+##### Examples
+
+{% include [x](../_includes/cast_examples.md) %}
+
+### Implicit casting {#implicit-cast}
+
+Implicit type casting that occurs in basic operations ( +-\*/) between different data types. The table cells specify the operation result type, if the operation is possible:
+
+#### Numeric types
+
+| Type | Int | Uint | Float | Double |
+| --- | --- | --- | --- | --- |
+| **INT** | — | `INT` | `Float` | `Double` |
+| **Uint** | `INT` | — | `Float` | `Double` |
+| **Float** | `Float` | `Float` | — | `Double` |
+| **Double** | `Double` | `Double` | `Double` | — |
+
+#### Date and time types
+
+| Type | Date | Datetime | Timestamp | Interval | TzDate | TzDatetime | TzTimestamp |
+| --- | --- | --- | --- | --- | --- | --- | --- |
+| **Date** | — | — | — | `Date` | — | — | — |
+| **Datetime** | — | — | — | `Datetime` | — | — | — |
+| **Timestamp** | — | — | — | `Timestamp` | — | — | — |
+| **Interval** | `Date` | `Datetime` | `Timestamp` | — | `TzDate` | `TzDatetime` | `TzTimestamp` |
+| **TzDate** | — | — | — | `TzDate` | — | — | — |
+| **TzDatetime** | — | — | — | `TzDatetime` | — | — | — |
+| **TzTimestamp** | — | — | — | `TzTimestamp` | — | — | — |
+
diff --git a/yql/essentials/docs/en/types/special.md b/yql/essentials/docs/en/types/special.md
new file mode 100644
index 00000000000..16a8cc7e019
--- /dev/null
+++ b/yql/essentials/docs/en/types/special.md
@@ -0,0 +1,14 @@
+# Special data types
+
+| Type | Description |
+| ----- | ----- |
+| `Callable` | A callable value that can be executed by passing arguments in parentheses in YQL SQL syntax or using the `Apply` function with the [s-expressions](/docs/s_expressions) syntax. |
+| `Resource` | Resource is an opaque pointer to a resource you can pass between the user defined functions (UDF). The type of the returned and accepted resource is declared inside a function using a string label. When passing a resource, YQL checks for label matching to prevent passing of resources between incompatible functions. If the labels mismatch, a type error occurs. |
+| `Tagged` | Tagged is the option to assign an application name to any other type. |
+| `Generic` | The data type used for data types. |
+| `Unit` | Unit is the data type used for non-enumerable entities (data sources and data sinks, atoms, etc.&nbsp;). |
+| `Null` | Void is a singular data type with the only possible null value. It's the type of the `NULL` literal and can be converted to any `Optional` type. |
+| `Void` | Void is a singular data type with the only possible `"null"` value. |
+| `EmptyList` | A singular data type with the only possible [] value. It's the type of the `[]` literal and can be converted to any `List` type. |
+| `EmptyDict` | A singular data type with the only possible {} value. It's a type of the `{}` literal and can be converted to any `Dict` or `Set` type. |
+
diff --git a/yql/essentials/docs/en/types/toc_i.yaml b/yql/essentials/docs/en/types/toc_i.yaml
new file mode 100644
index 00000000000..060dd02891f
--- /dev/null
+++ b/yql/essentials/docs/en/types/toc_i.yaml
@@ -0,0 +1,17 @@
+items:
+- name: Overview
+ href: index.md
+- name: Simple
+ href: primitive.md
+- name: Optional
+ href: optional.md
+- name: Containers
+ href: containers.md
+- name: Special
+ href: special.md
+- name: Type casting
+ href: cast.md
+- name: Text representation of data types
+ href: type_string.md
+- name: JSON
+ href: json.md \ No newline at end of file
diff --git a/yql/essentials/docs/en/types/type_string.md b/yql/essentials/docs/en/types/type_string.md
new file mode 100644
index 00000000000..e8a0c54d7d0
--- /dev/null
+++ b/yql/essentials/docs/en/types/type_string.md
@@ -0,0 +1,70 @@
+# Text representation of data types
+
+## Introduction {#intro}
+
+Since YQL is a strongly typed language, the data type is important for many of its aspects. To make data type management easy, YQL has a data type definition convention in text format. It's mentioned in many places in the documentation. There's also a library that provides functions for building a data type based on a text description (for example, when manually defining the signature for the called value) or for serializing the data type into a string for debugging purposes.
+
+Functions for data types are [described in the article](../builtins/types.md). Below is the format of text representation of data types.
+
+## General conventions {#rules}
+
+* [Primitive data types](primitive.md) are represented in text format simply by referencing their name.
+* A complex data type is composed of other data types. If you depict this structure as a tree, it has [primitive data types](primitive.md) as leaves and [containers](containers.md) as other nodes. [You may treat special data types](special.md) as exceptions, because they can function as both.
+* The text representation repeats the structure of this tree from the root to the leaves: each node of the tree specifies the name of the current data type, and proceeding to a deeper level is denoted by different types of brackets.
+* Feel free to use spaces and line breaks if they improve readability.
+* If the ID contains something else except the Latin letters and numbers, put it in single quotes and use C-escaping.
+
+## Containers {#containers}
+
+* Use angle brackets to specify the types of container elements.
+
+ Example: `List<Int32>`.
+
+* If a container can hold multiple heterogeneous elements, they are listed inside angle brackets with a comma.
+
+ Example: `Tuple<Int32, String>`.
+
+* If a container can hold named elements, use comma-separated name-type pairs with a colon in-between instead of comma-separated data types.
+
+ Example: `Struct<a:Int32, b:String>`.
+
+* The underlying `Variant` container type is chosen based on the presence of names in arguments.
+
+ Example: `Variant<Int32, String>` is a variant on tuple, `Variant<a:Int32, b:String>` is a variant on structure.
+
+## Types that allow NULL {#optional}
+
+* They are called `Optional` in YQL terms, or nullable in the classic SQL terms.
+
+* Formally, this type is a container. So, you may declare it as `Optional<...>`, but the shortcut notation of a question mark suffix is usually used instead.
+
+ Example: `String?`.
+
+## Called values {#callable}
+
+* The basic form of the called values looks as follows: `(arg1, arg2, ...) -> result`.
+
+ An example of declaring a function signature that accepts two strings and returns a number: `(String, String) -> Int64`.
+
+* The called values can return the called values: in this case, they make up a chain of the required length.
+
+ Example: `(String, String) -> (String, String) -> Int64`.
+
+* Optional arguments must have the `Optional` type at the top level and be enclosed in square brackets.
+
+ Example: `(String, [String?, Double?]) -> Int64`.
+
+* The arguments of the called values can contain flags.
+
+ Currently, the only possible flag is `AutoMap`. It means that if NULL is passed to this argument, the result must also be set to NULL without running the function.
+
+ Example: `(String{Flags: AutoMap}) -> Int64`.
+
+* Use this particular format if you need `Optional<Callable...>`, because the trailing question mark refers to the result of the called value.
+
+## Resources {#resources}
+
+* Unlike containers, a resource isn't parameterized by the element type (it's a pointer in memory and YQL knows nothing about its contents). Instead, a resource is parameterized by a string label that can safeguard against passing resources between incompatible functions.
+
+ Example: `Resource<Foo>`.
+
diff --git a/yql/essentials/docs/en/udf/list/datetime.md b/yql/essentials/docs/en/udf/list/datetime.md
new file mode 100644
index 00000000000..c18380d4082
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/datetime.md
@@ -0,0 +1,501 @@
+# DateTime
+
+In the DateTime module, the main internal representation format is `Resource<TM>`, which stores the following date components:
+
+* Year (12 bits).
+* Month (4 bits).
+* Day (5 bits).
+* Hour (5 bits).
+* Minute (6 bits).
+* Second (6 bits).
+* Microsecond (20 bits).
+* TimezoneId (16 bits).
+* DayOfYear (9 bits): Day since the beginning of the year.
+* WeekOfYear (6 bits): Week since the beginning of the year, January 1 is always in week 1.
+* WeekOfYearIso8601 (6 bits): Week of the year according to ISO 8601 (the first week is the one that includes January 4).
+* DayOfWeek (3 bits): Day of the week.
+
+If the timezone is not GMT, the components store the local time for the relevant timezone.
+
+## Split {#split}
+
+Conversion from a primitive type to an internal representation. It's always successful on a non-empty input.
+
+### List of functions
+
+* `DateTime::Split(Date{Flags:AutoMap}) -> Resource<TM>`
+* `DateTime::Split(Datetime{Flags:AutoMap}) -> Resource<TM>`
+* `DateTime::Split(Timestamp{Flags:AutoMap}) -> Resource<TM>`
+* `DateTime::Split(TzDate{Flags:AutoMap}) -> Resource<TM>`
+* `DateTime::Split(TzDatetime{Flags:AutoMap}) -> Resource<TM>`
+* `DateTime::Split(TzTimestamp{Flags:AutoMap}) -> Resource<TM>`
+
+Functions that accept `Resource<TM>` as input, can be called directly from the primitive date/time type. An implicit conversion will be made in this case by calling a relevant `Split` function.
+
+## Make... {#make}
+
+Making a primitive type from an internal representation. It's always successful on a non-empty input.
+
+### List of functions
+
+* `DateTime::MakeDate(Resource<TM>{Flags:AutoMap}) -> Date`
+* `DateTime::MakeDatetime(Resource<TM>{Flags:AutoMap}) -> Datetime`
+* `DateTime::MakeTimestamp(Resource<TM>{Flags:AutoMap}) -> Timestamp`
+* `DateTime::MakeTzDate(Resource<TM>{Flags:AutoMap}) -> TzDate`
+* `DateTime::MakeTzDatetime(Resource<TM>{Flags:AutoMap}) -> TzDatetime`
+* `DateTime::MakeTzTimestamp(Resource<TM>{Flags:AutoMap}) -> TzTimestamp`
+
+### Examples
+
+```yql
+SELECT
+ DateTime::MakeTimestamp(DateTime::Split(Datetime("2019-01-01T15:30:00Z"))),
+ -- 2019-01-01T15:30:00.000000Z
+ DateTime::MakeDate(Datetime("2019-01-01T15:30:00Z")),
+ -- 2019-01-01
+ DateTime::MakeTimestamp(DateTime::Split(TzDatetime("2019-01-01T00:00:00,Europe/Moscow"))),
+ -- 2018-12-31T21:00:00Z (conversion to UTC)
+ DateTime::MakeDate(TzDatetime("2019-01-01T12:00:00,GMT"))
+ -- 2019-01-01 (Datetime -> Date with implicit Split)>
+```
+
+## Get... {#get}
+
+Extracting a component from an internal representation.
+
+### List of functions
+
+* `DateTime::GetYear(Resource<TM>{Flags:AutoMap}) -> Uint16`
+* `DateTime::GetDayOfYear(Resource<TM>{Flags:AutoMap}) -> Uint16`
+* `DateTime::GetMonth(Resource<TM>{Flags:AutoMap}) -> Uint8`
+* `DateTime::GetMonthName(Resource<TM>{Flags:AutoMap}) -> String`
+* `DateTime::GetWeekOfYear(Resource<TM>{Flags:AutoMap}) -> Uint8`
+* `DateTime::GetWeekOfYearIso8601(Resource<TM>{Flags:AutoMap}) -> Uint8`
+* `DateTime::GetDayOfMonth(Resource<TM>{Flags:AutoMap}) -> Uint8`
+* `DateTime::GetDayOfWeek(Resource<TM>{Flags:AutoMap}) -> Uint8`
+* `DateTime::GetDayOfWeekName(Resource<TM>{Flags:AutoMap}) -> String`
+* `DateTime::GetHour(Resource<TM>{Flags:AutoMap}) -> Uint8`
+* `DateTime::GetMinute(Resource<TM>{Flags:AutoMap}) -> Uint8`
+* `DateTime::GetSecond(Resource<TM>{Flags:AutoMap}) -> Uint8`
+* `DateTime::GetMillisecondOfSecond(Resource<TM>{Flags:AutoMap}) -> Uint32`
+* `DateTime::GetMicrosecondOfSecond(Resource<TM>{Flags:AutoMap}) -> Uint32`
+* `DateTime::GetTimezoneId(Resource<TM>{Flags:AutoMap}) -> Uint16`
+* `DateTime::GetTimezoneName(Resource<TM>{Flags:AutoMap}) -> String`
+
+### Examples
+
+```yql
+$tm = DateTime::Split(TzDatetime("2019-01-09T00:00:00,Europe/Moscow"));
+
+SELECT
+ DateTime::GetDayOfMonth($tm) as Day, -- 9
+ DateTime::GetMonthName($tm) as Month, -- "January"
+ DateTime::GetYear($tm) as Year, -- 2019
+ DateTime::GetTimezoneName($tm) as TzName, -- "Europe/Moscow"
+ DateTime::GetDayOfWeekName($tm) as WeekDay; -- "Wednesday"
+```
+
+## Update {#update}
+
+Updating one or more components in the internal representation. Returns either an updated copy or NULL, if an update produces an invalid date or other inconsistencies.
+
+### List of functions
+
+```yql
+DateTime::Update( Resource<TM>{Flags:AutoMap}, [ Year:Uint16?, Month:Uint8?, Day:Uint8?, Hour:Uint8?, Minute:Uint8?, Second:Uint8?, Microsecond:Uint32?, Timezone:String? ]) -> Resource<TM>?
+```
+
+### Examples
+
+```yql
+$tm = DateTime::Split(Timestamp("2019-01-01T01:02:03.456789Z"));
+
+SELECT
+ DateTime::MakeDate(DateTime::Update($tm, 2012)), -- 2012-01-01
+ DateTime::MakeDate(DateTime::Update($tm, 2000, 6, 6)), -- 2000-06-06
+ DateTime::MakeDate(DateTime::Update($tm, NULL, 2, 30)), -- NULL (February 30)
+ DateTime::MakeDatetime(DateTime::Update($tm, NULL, NULL, 31)), -- 2019-01-31T01:02:03Z
+ DateTime::MakeDatetime(DateTime::Update($tm, 15 as Hour, 30 as Minute)), -- 2019-01-01T15:30:03Z
+ DateTime::MakeTimestamp(DateTime::Update($tm, 999999 as Microsecond)), -- 2019-01-01T01:02:03.999999Z
+ DateTime::MakeTimestamp(DateTime::Update($tm, "Europe/Moscow" as Timezone)), -- 2018-12-31T22:02:03.456789Z (conversion to UTC)
+ DateTime::MakeTzTimestamp(DateTime::Update($tm, "Europe/Moscow" as Timezone)); -- 2019-01-01T01:02:03.456789,Europe/Moscow
+```
+
+## From... {#from}
+
+Getting a Timestamp from the number of seconds/milliseconds/microseconds since the UTC epoch. When the Timestamp limits are exceeded, NULL is returned.
+
+### List of functions
+
+* `DateTime::FromSeconds(Uint32{Flags:AutoMap}) -> Timestamp`
+* `DateTime::FromMilliseconds(Uint64{Flags:AutoMap}) -> Timestamp`
+* `DateTime::FromMicroseconds(Uint64{Flags:AutoMap}) -> Timestamp`
+
+## To... {#to}
+
+Getting a number of seconds/milliseconds/microseconds since the UTC Epoch from a primitive type.
+
+### List of functions
+
+* `DateTime::ToSeconds(Date/DateTime/Timestamp/TzDate/TzDatetime/TzTimestamp{Flags:AutoMap}) -> Uint32`
+* `DateTime::ToMilliseconds(Date/DateTime/Timestamp/TzDate/TzDatetime/TzTimestamp{Flags:AutoMap}) -> Uint64`
+* `DateTime::ToMicroseconds(Date/DateTime/Timestamp/TzDate/TzDatetime/TzTimestamp{Flags:AutoMap}) -> Uint64`
+
+### Examples
+
+```yql
+SELECT
+ DateTime::FromSeconds(1546304523), -- 2019-01-01T01:02:03.000000Z
+ DateTime::ToMicroseconds(Timestamp("2019-01-01T01:02:03.456789Z")); -- 1546304523456789
+```
+
+## Interval... {#interval}
+
+Conversions between `Interval` and various time units.
+
+### List of functions
+
+* `DateTime::ToDays(Interval{Flags:AutoMap}) -> Int16`
+* `DateTime::ToHours(Interval{Flags:AutoMap}) -> Int32`
+* `DateTime::ToMinutes(Interval{Flags:AutoMap}) -> Int32`
+* `DateTime::ToSeconds(Interval{Flags:AutoMap}) -> Int32`
+* `DateTime::ToMilliseconds(Interval{Flags:AutoMap}) -> Int64`
+* `DateTime::ToMicroseconds(Interval{Flags:AutoMap}) -> Int64`
+* `DateTime::IntervalFromDays(Int16{Flags:AutoMap}) -> Interval`
+* `DateTime::IntervalFromHours(Int32{Flags:AutoMap}) -> Interval`
+* `DateTime::IntervalFromMinutes(Int32{Flags:AutoMap}) -> Interval`
+* `DateTime::IntervalFromSeconds(Int32{Flags:AutoMap}) -> Interval`
+* `DateTime::IntervalFromMilliseconds(Int64{Flags:AutoMap}) -> Interval`
+* `DateTime::IntervalFromMicroseconds(Int64{Flags:AutoMap}) -> Interval`
+
+AddTimezone doesn't affect the output of ToSeconds() in any way, because ToSeconds() always returns GMT time.
+
+You can also create an Interval from a string literal in the format [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601%23Durations).
+
+### Examples
+
+```yql
+SELECT
+ DateTime::ToDays(Interval("PT3000M")), -- 2
+ DateTime::IntervalFromSeconds(1000000), -- 11 days 13 hours 46 minutes 40 seconds
+ DateTime::ToDays(cast('2018-01-01' as date) - cast('2017-12-31' as date)); --1
+```
+
+## StartOf... / EndOf... / TimeOfDay {#startof}
+
+Get the start (end) of the period including the date/time. If the result is invalid, NULL is returned. If the timezone is different from GMT, then the period start (end) is in the specified time zone.
+
+### List of functions
+
+* `DateTime::StartOfYear(Resource<TM>{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::EndOfYear(Resource<TM>{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::StartOfQuarter(Resource<TM>{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::EndOfQuarter(Resource<TM>{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::StartOfMonth(Resource<TM>{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::EndOfMonth(Resource<TM>{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::StartOfWeek(Resource<TM>{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::EndOfWeek(Resource<TM>{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::StartOfDay(Resource<TM>{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::EndOfDay(Resource<TM>{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::StartOf(Resource<TM>{Flags:AutoMap}, Interval{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::EndOf(Resource<TM>{Flags:AutoMap}, Interval{Flags:AutoMap}) -> Resource<TM>?`
+
+The `StartOf`/`EndOf` functions are intended for grouping by an arbitrary period within a day. The result differs from the input value only by time components. A period exceeding one day is treated as a day (an equivalent of `StartOfDay`/`EndOfDay`). If a day doesn't include an integer number of periods, the number is rounded to the nearest time from the beginning of the day that is a multiple of the specified period. When the interval is zero, the output is same as the input. A negative interval is treated as a positive one.
+
+The `EndOf...` functions are intended for obtaining the latest moment in the same period of time as the specified one.
+
+The functions treat periods longer than one day in a different manner than the same-name functions in the old library. The time components are always reset to zero (this makes sense, because these functions are mainly used for grouping by the period). You can also specify a time period within a day:
+
+* `DateTime::TimeOfDay(Resource<TM>{Flags:AutoMap}) -> Interval`
+
+### Examples
+
+```yql
+SELECT
+ DateTime::MakeDate(DateTime::StartOfYear(Date("2019-06-06"))),
+ -- 2019-01-01 (implicit Split here and below)
+ DateTime::MakeDatetime(DateTime::StartOfQuarter(Datetime("2019-06-06T01:02:03Z"))),
+ -- 2019-04-01T00:00:00Z (time components are reset to zero)
+ DateTime::MakeDate(DateTime::StartOfMonth(Timestamp("2019-06-06T01:02:03.456789Z"))),
+ -- 2019-06-01
+ DateTime::MakeDate(DateTime::StartOfWeek(Date("1970-01-01"))),
+ -- NULL (the beginning of the epoch is Thursday, the beginning of the week is 1969-12-29 that is beyond the limits)
+ DateTime::MakeTimestamp(DateTime::StartOfWeek(Date("2019-01-01"))),
+ -- 2018-12-31T00:00:00Z
+ DateTime::MakeDatetime(DateTime::StartOfDay(Datetime("2019-06-06T01:02:03Z"))),
+ -- 2019-06-06T00:00:00Z
+ DateTime::MakeTzDatetime(DateTime::StartOfDay(TzDatetime("1970-01-01T05:00:00,Europe/Moscow"))),
+ -- NULL (beyond the epoch in GMT)
+ DateTime::MakeTzTimestamp(DateTime::StartOfDay(TzTimestamp("1970-01-02T05:00:00.000000,Europe/Moscow"))),
+ -- 1970-01-02T00:00:00,Europe/Moscow (the beginning of the day in Moscow)
+ DateTime::MakeDatetime(DateTime::StartOf(Datetime("2019-06-06T23:45:00Z"), Interval("PT7H"))),
+ -- 2019-06-06T21:00:00Z
+ DateTime::MakeDatetime(DateTime::StartOf(Datetime("2019-06-06T23:45:00Z"), Interval("PT20M"))),
+ -- 2019-06-06T23:40:00Z
+ DateTime::TimeOfDay(Timestamp("2019-02-14T01:02:03.456789Z"));
+ -- 1 hour 2 minutes 3 seconds 456789 microseconds
+```
+
+## Shift... {#shift}
+
+Add/subtract the specified number of units to/from the component in the internal representation and update the other fields.
+Returns either an updated copy or NULL, if an update produces an invalid date or other inconsistencies.
+
+### List of functions
+
+* `DateTime::ShiftYears(Resource<TM>{Flags:AutoMap}, Int32) -> Resource<TM>?`
+* `DateTime::ShiftQuarters(Resource<TM>{Flags:AutoMap}, Int32) -> Resource<TM>?`
+* `DateTime::ShiftMonths(Resource<TM>{Flags:AutoMap}, Int32) -> Resource<TM>?`
+
+If the resulting number of the day in the month exceeds the maximum allowed, then the `Day` field will accept the last day of the month without changing the time (see examples).
+
+### Examples
+
+```yql
+$tm1 = DateTime::Split(DateTime("2019-01-31T01:01:01Z"));
+$tm2 = DateTime::Split(TzDatetime("2049-05-20T12:34:50,Europe/Moscow"));
+
+SELECT
+ DateTime::MakeDate(DateTime::ShiftYears($tm1, 10)), -- 2029-01-31T01:01:01
+ DateTime::MakeDate(DateTime::ShiftYears($tm2, -10000)), -- NULL (beyond the limits)
+ DateTime::MakeDate(DateTime::ShiftQuarters($tm2, 0)), -- 2049-05-20T12:34:50,Europe/Moscow
+ DateTime::MakeDate(DateTime::ShiftQuarters($tm1, -3)), -- 2018-04-30T01:01:01
+ DateTime::MakeDate(DateTime::ShiftMonths($tm1, 1)), -- 2019-02-28T01:01:01
+ DateTime::MakeDate(DateTime::ShiftMonths($tm1, -35)), -- 2016-02-29T01:01:01
+```
+
+## Format {#format}
+
+Get a string representation of a time using an arbitrary formatting string.
+
+### List of functions
+
+* `DateTime::Format(String, alwaysWriteFractionalSeconds:Bool?) -> (Resource<TM>{Flags:AutoMap}) -> String`
+
+A set of specifiers is implemented for the formatting string:
+
+* `%%`: % character.
+* `%Y`: 4-digit year.
+* `%m`: 2-digit month.
+* `%d`: 2-digit day.
+* `%H`: 2-digit hour.
+* `%M`: 2-digit minutes.
+* `%S`: 2-digit seconds -- or xx.xxxxxx in the case of non-empty microseconds (only if `alwaysWriteFractionalSeconds` is not set to `True`).
+* `%z`: +hhmm or -hhmm.
+* `%Z`: IANA name of the timezone.
+* `%b`: A short three-letter English name of the month (Jan).
+* `%B`: A full English name of the month (January).
+
+All other characters in the format string are passed on without changes.
+
+### Examples
+
+```yql
+$format = DateTime::Format("%Y-%m-%d %H:%M:%S %Z");
+
+SELECT
+ $format(DateTime::Split(TzDatetime("2019-01-01T01:02:03,Europe/Moscow")));
+ -- "2019-01-01 01:02:03 Europe/Moscow"
+```
+
+## Parse {#parse}
+
+Parse a string into an internal representation using an arbitrary formatting string. Default values are used for empty fields. If errors are raised, NULL is returned.
+
+### List of functions
+
+* `DateTime::Parse(String) -> (String{Flags:AutoMap}) -> Resource<TM>?`
+
+Implemented specifiers:
+
+* `%%`: the % character.
+* `%Y`: 4-digit year (1970).
+* `%m`: 2-digit month (1).
+* `%d`: 2-digit day (1).
+* `%H`: 2-digit hour (0).
+* `%M`: 2-digit minutes (0).
+* `%S`: Seconds (0), can also accept microseconds in the formats from xx. up to xx.xxxxxx
+* `%Z`: The IANA name of the timezone (GMT).
+* `%b`: A short three-letter case-insensitive English name of the month (Jan).
+* `%B`: A full case-insensitive English name of the month (January).
+
+### Examples
+
+```yql
+$parse1 = DateTime::Parse("%H:%M:%S");
+$parse2 = DateTime::Parse("%S");
+$parse3 = DateTime::Parse("%m/%d/%Y");
+$parse4 = DateTime::Parse("%Z");
+
+SELECT
+ DateTime::MakeDatetime($parse1("01:02:03")), -- 1970-01-01T01:02:03Z
+ DateTime::MakeTimestamp($parse2("12.3456")), -- 1970-01-01T00:00:12.345600Z
+ DateTime::MakeTimestamp($parse3("02/30/2000")), -- NULL (Feb 30)
+ DateTime::MakeTimestamp($parse4("Canada/Central")); -- 1970-01-01T06:00:00Z (conversion to UTC)
+```
+
+For the common formats, wrappers around the corresponding util methods are supported. You can only get TM with components in the UTC timezone.
+
+## Parse specific formats
+
+### List of functions
+
+* `DateTime::ParseRfc822(String{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::ParseIso8601(String{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::ParseHttp(String{Flags:AutoMap}) -> Resource<TM>?`
+* `DateTime::ParseX509(String{Flags:AutoMap}) -> Resource<TM>?`
+
+### Examples
+
+```yql
+SELECT
+ DateTime::MakeTimestamp(DateTime::ParseRfc822("Fri, 4 Mar 2005 19:34:45 EST")),
+ -- 2005-03-05T00:34:45Z
+ DateTime::MakeTimestamp(DateTime::ParseIso8601("2009-02-14T02:31:30+0300")),
+ -- 2009-02-13T23:31:30Z
+ DateTime::MakeTimestamp(DateTime::ParseHttp("Sunday, 06-Nov-94 08:49:37 GMT")),
+ -- 1994-11-06T08:49:37Z
+ DateTime::MakeTimestamp(DateTime::ParseX509("20091014165533Z"))
+ -- 2009-10-14T16:55:33Z
+```
+
+## Standard scenarios
+
+### Conversions between strings and seconds
+
+Converting a string date (in the Moscow timezone) to seconds (in GMT timezone):
+
+```yql
+$datetime_parse = DateTime::Parse("%Y-%m-%d %H:%M:%S");
+$datetime_parse_tz = DateTime::Parse("%Y-%m-%d %H:%M:%S %Z");
+
+SELECT
+ DateTime::ToSeconds(TzDateTime("2019-09-16T00:00:00,Europe/Moscow")) AS md_us1, -- 1568581200
+ DateTime::ToSeconds(DateTime::MakeDatetime($datetime_parse_tz("2019-09-16 00:00:00" || " Europe/Moscow"))), -- 1568581200
+ DateTime::ToSeconds(DateTime::MakeDatetime(DateTime::Update($datetime_parse("2019-09-16 00:00:00"), "Europe/Moscow" as Timezone))), -- 1568581200
+
+ -- INCORRECT (Date imports time as GMT, but AddTimezone has no effect on ToSeconds that always returns GMT time)
+ DateTime::ToSeconds(AddTimezone(Date("2019-09-16"), 'Europe/Moscow')) AS md_us2, -- 1568592000
+```
+
+Converting a string date (in the Moscow timezone) to seconds (in the Moscow timezone). DateTime::ToSeconds() exports only to GMT. That's why we should put timezones aside for a while and use only GMT (as if we assumed for a while that Moscow is in GMT):
+
+```yql
+$date_parse = DateTime::Parse("%Y-%m-%d");
+$datetime_parse = DateTime::Parse("%Y-%m-%d %H:%M:%S");
+$datetime_parse_tz = DateTime::Parse("%Y-%m-%d %H:%M:%S %Z");
+
+SELECT
+ DateTime::ToSeconds(Datetime("2019-09-16T00:00:00Z")) AS md_ms1, -- 1568592000
+ DateTime::ToSeconds(Date("2019-09-16")) AS md_ms2, -- 1568592000
+ DateTime::ToSeconds(DateTime::MakeDatetime($date_parse("2019-09-16"))) AS md_ms3, -- 1568592000
+ DateTime::ToSeconds(DateTime::MakeDatetime($datetime_parse("2019-09-16 00:00:00"))) AS md_ms4, -- 1568592000
+ DateTime::ToSeconds(DateTime::MakeDatetime($datetime_parse_tz("2019-09-16 00:00:00 GMT"))) AS md_ms5, -- 1568592000
+
+ -- INCORRECT (imports the time in the Moscow timezone, but RemoveTimezone doesn't affect ToSeconds in any way)
+ DateTime::ToSeconds(RemoveTimezone(TzDatetime("2019-09-16T00:00:00,Europe/Moscow"))) AS md_ms6, -- 1568581200
+ DateTime::ToSeconds(DateTime::MakeDatetime($datetime_parse_tz("2019-09-16 00:00:00 Europe/Moscow"))) AS md_ms7 -- 1568581200
+```
+
+Converting seconds (in the GMT timezone) to a string date (in the Moscow timezone):
+
+```yql
+$date_format = DateTime::Format("%Y-%m-%d %H:%M:%S %Z");
+SELECT
+ $date_format(AddTimezone(DateTime::FromSeconds(1568592000), 'Europe/Moscow')) -- "2019-09-16 03:00:00 Europe/Moscow"
+```
+
+Converting seconds (in the Moscow timezone) to a string date (in the Moscow timezone). In this case, the %Z timezone is output for reference: usually, it's not needed because it's "GMT" and might mislead you.
+
+```yql
+$date_format = DateTime::Format("%Y-%m-%d %H:%M:%S %Z");
+SELECT
+ $date_format(DateTime::FromSeconds(1568592000)) -- "2019-09-16 00:00:00 GMT"
+```
+
+Converting seconds (in the GMT timezone) to three-letter days of the week (in the Moscow timezone):
+
+```yql
+SELECT
+ SUBSTRING(DateTime::GetDayOfWeekName(AddTimezone(DateTime::FromSeconds(1568581200), "Europe/Moscow")), 0, 3) -- "Mon"
+```
+
+### Date and time formatting
+
+Usually a separate named expression is used to format time, but you can do without it:
+
+```yql
+$date_format = DateTime::Format("%Y-%m-%d %H:%M:%S %Z");
+
+SELECT
+
+ -- A variant with a named expression
+
+ $date_format(AddTimezone(DateTime::FromSeconds(1568592000), 'Europe/Moscow')),
+
+ -- A variant without a named expression
+
+ DateTime::Format("%Y-%m-%d %H:%M:%S %Z")
+ (AddTimezone(DateTime::FromSeconds(1568592000), 'Europe/Moscow'))
+;
+```
+
+### Converting types
+
+This way, you can convert only constants:
+
+```yql
+SELECT
+ TzDateTime("2019-09-16T00:00:00,Europe/Moscow"), -- 2019-09-16T00:00:00,Europe/Moscow
+ Date("2019-09-16") -- 2019-09-16
+```
+
+But this way, you can convert a constant, a named expression, or a table field:
+
+```yql
+SELECT
+ CAST("2019-09-16T00:00:00,Europe/Moscow" AS TzDateTime), -- 2019-09-16T00:00:00,Europe/Moscow
+ CAST("2019-09-16" AS Date) -- 2019-09-16
+```
+
+### Converting time to date
+
+A CAST to Date or TzDate outputs a GMT date for a midnight, local time (for example, for Moscow time 2019-10-22 00:00:00, the date 2019-10-21 is returned). To get a date in the local timezone, you can use DateTime::Format.
+
+```yql
+$x = DateTime("2019-10-21T21:00:00Z");
+SELECT
+ AddTimezone($x, "Europe/Moscow"), -- 2019-10-22T00:00:00,Europe/Moscow
+ cast($x as TzDate), -- 2019-10-21,GMT
+ cast(AddTimezone($x, "Europe/Moscow") as TzDate), -- 2019-10-21,Europe/Moscow
+ cast(AddTimezone($x, "Europe/Moscow") as Date), -- 2019-10-21
+ DateTime::Format("%Y-%m-%d %Z")(AddTimezone($x, "Europe/Moscow")), -- 2019-10-22 Europe/Moscow
+```
+
+It's worth mentioning that several `TzDatetime` or `TzTimestamp` values with a positive timezone offset cannot be cast to `TzDate`. Consider the example below:
+
+```yql
+SELECT CAST(TzDatetime("1970-01-01T23:59:59,Europe/Moscow") as TzDate);
+/* Fatal: Timestamp 1970-01-01T23:59:59.000000,Europe/Moscow cannot be casted to TzDate */
+```
+
+Starting from the Unix epoch, there is no valid value representing midnight on 01/01/1970 for the Europe/Moscow timezone. As a result, such a cast is impossible and fails at runtime.
+
+At the same time, values with a negative timezone offset are converted correctly:
+
+```yql
+SELECT CAST(TzDatetime("1970-01-01T23:59:59,America/Los_Angeles") as TzDate);
+/* 1970-01-01,America/Los_Angeles */
+```
+
+### Daylight saving time
+
+Please note that daylight saving time depends on the year:
+
+```yql
+SELECT
+ RemoveTimezone(TzDatetime("2019-09-16T10:00:00,Europe/Moscow")) as DST1, -- 2019-09-16T07:00:00Z
+ RemoveTimezone(TzDatetime("2008-12-03T10:00:00,Europe/Moscow")) as DST2, -- 2008-12-03T07:00:00Z
+ RemoveTimezone(TzDatetime("2008-07-03T10:00:00,Europe/Moscow")) as DST3, -- 2008-07-03T06:00:00Z (DST)
+```
+
diff --git a/yql/essentials/docs/en/udf/list/digest.md b/yql/essentials/docs/en/udf/list/digest.md
new file mode 100644
index 00000000000..2acd4de863a
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/digest.md
@@ -0,0 +1,43 @@
+# Digest
+
+A set of commonly used hash functions.
+
+## List of functions
+
+* `Digest::Crc32c(String{Flags::AutoMap}) -> Uint32`
+* `Digest::Crc64(String{Flags::AutoMap}) -> Uint64`
+* `Digest::Fnv32(String{Flags::AutoMap}) -> Uint32`
+* `Digest::Fnv64(String{Flags::AutoMap}) -> Uint64`
+* `Digest::MurMurHash(String{Flags:AutoMap}) -> Uint64`
+* `Digest::MurMurHash32(String{Flags:AutoMap}) -> Uint32`
+* `Digest::MurMurHash2A(String{Flags:AutoMap}) -> Uint64`
+* `Digest::MurMurHash2A32(String{Flags:AutoMap}) -> Uint32`
+* `Digest::CityHash(String{Flags:AutoMap}, [Uint64?]) -> Uint64`: The second optional argument is seed
+* `Digest::CityHash128(String{Flags:AutoMap}) -> Tuple<Uint64,Uint64>`
+* `Digest::NumericHash(Uint64{Flags:AutoMap}) -> Uint64`
+* `Digest::Md5Hex(String{Flags:AutoMap}) -> String`
+* `Digest::Md5Raw(String{Flags:AutoMap}) -> String`
+* `Digest::Md5HalfMix(String{Flags:AutoMap}) -> Uint64`: MD5 coarsening option (yabs_md5)
+* `Digest::Argon2(String{Flags:AutoMap},String{Flags:AutoMap}) -> String`: The second argument is the salt
+* `Digest::Blake2B(String{Flags:AutoMap},[String?]) -> String`: The second optional argument is the key
+* `Digest::SipHash(Uint64,Uint64,String{Flags:AutoMap}) -> Uint64`
+* `Digest::HighwayHash(Uint64,Uint64,Uint64,Uint64,String{Flags:AutoMap}) -> Uint64`
+* `Digest::FarmHashFingerprint(Uint64{Flags:AutoMap}) -> Uint64`
+* `Digest::FarmHashFingerprint2(Uint64{Flags:AutoMap}, Uint64{Flags:AutoMap}) -> Uint64`
+* `Digest::FarmHashFingerprint32(String{Flags:AutoMap}) -> Uint32`
+* `Digest::FarmHashFingerprint64(String{Flags:AutoMap}) -> Uint64`
+* `Digest::FarmHashFingerprint128(String{Flags:AutoMap}) -> Tuple<Uint64,Uint64>`
+* `Digest::SuperFastHash(String{Flags:AutoMap}) -> Uint32`
+* `Digest::Sha1(String{Flags:AutoMap}) -> String`
+* `Digest::Sha256(String{Flags:AutoMap}) -> String`
+* `Digest::IntHash64(Uint64{Flags:AutoMap}) -> Uint64`
+* `Digest::XXH3(String{Flags:AutoMap}) -> Uint64`
+* `Digest::XXH3_128(String{Flags:AutoMap}) -> Tuple<Uint64,Uint64>`
+
+### Examples
+
+```yql
+SELECT Digest::Md5Hex("YQL"); -- "1a0c1b56e9d617688ee345da4030da3c"
+SELECT Digest::NumericHash(123456789); -- 1734215268924325803
+```
+
diff --git a/yql/essentials/docs/en/udf/list/histogram.md b/yql/essentials/docs/en/udf/list/histogram.md
new file mode 100644
index 00000000000..94ac054c79f
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/histogram.md
@@ -0,0 +1,19 @@
+# Histogram
+
+Set of auxiliary functions for the [HISTOGRAM aggregate function](../../builtins/aggregation.md). In the signature description below, HistogramStruct refers to the result of the aggregate function `HISTOGRAM`, `LinearHistogram` or `LogarithmicHistogram` being a structure of a certain type.
+
+## List of functions
+
+* `Histogram::Print(HistogramStruct{Flags:AutoMap}, Byte?) -> String`
+* `Histogram::Normalize(HistogramStruct{Flags:AutoMap}, [Double?]) -> HistogramStruct`: The second argument specifies the desired area of the histogram, 100 by default.
+* `Histogram::ToCumulativeDistributionFunction(HistogramStruct{Flags:AutoMap}) -> HistogramStruct`
+* `Histogram::GetSumAboveBound(HistogramStruct{Flags:AutoMap}, Double) -> Double`
+* `Histogram::GetSumBelowBound(HistogramStruct{Flags:AutoMap}, Double) -> Double`
+* `Histogram::GetSumInRange(HistogramStruct{Flags:AutoMap}, Double, Double) -> Double`
+* `Histogram::CalcUpperBound(HistogramStruct{Flags:AutoMap}, Double) -> Double`
+* `Histogram::CalcLowerBound(HistogramStruct{Flags:AutoMap}, Double) -> Double`
+* `Histogram::CalcUpperBoundSafe(HistogramStruct{Flags:AutoMap}, Double) -> Double`
+* `Histogram::CalcLowerBoundSafe(HistogramStruct{Flags:AutoMap}, Double) -> Double`
+
+`Histogram::Print` has an optional numeric argument that sets the maximum length of the histogram columns (the length is in characters, since the histogram is rendered in ASCII art). Default: 25. This function is primarily intended for viewing histograms in the console.
+
diff --git a/yql/essentials/docs/en/udf/list/hyperscan.md b/yql/essentials/docs/en/udf/list/hyperscan.md
new file mode 100644
index 00000000000..9bb32a078d7
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/hyperscan.md
@@ -0,0 +1,108 @@
+# Hyperscan
+
+[Hyperscan](https://www.hyperscan.io) is an opensource library for regular expression matching developed by Intel.
+
+The library includes 4 implementations that use different sets of processor instructions (SSE3, SSE4.2, AVX2, and AVX512), with the needed instruction automatically selected based on the current processor.
+
+By default, all functions work in the single-byte mode. However, if the regular expression is a valid UTF-8 string but is not a valid ASCII string, the UTF-8 mode is enabled automatically.
+
+## List of functions
+
+* `Hyperscan::Grep(pattern:String) -> (string:String?) -> Bool`
+* `Hyperscan::Match(pattern:String) -> (string:String?) -> Bool`
+* `Hyperscan::BacktrackingGrep(pattern:String) -> (string:String?) -> Bool`
+* `Hyperscan::BacktrackingMatch(pattern:String) -> (string:String?) -> Bool`
+* `Hyperscan::MultiGrep(pattern:String) -> (string:String?) -> Tuple<Bool, Bool, ...>`
+* `Hyperscan::MultiMatch(pattern:String) -> (string:String?) -> Tuple<Bool, Bool, ...>`
+* `Hyperscan::Capture(pattern:String) -> (string:String?) -> String?`
+* `Hyperscan::Replace(pattern:String) -> (string:String?, replacement:String) -> String?`
+
+## Call syntax {#syntax}
+
+To avoid compiling a regular expression at each table row at direct call, wrap the function call by [a named expression](../../syntax/expressions.md#named-nodes):
+
+```yql
+$re = Hyperscan::Grep("\\d+"); -- create a callable value to match a specific regular expression
+SELECT * FROM table WHERE $re(key); -- use it to filter the table
+```
+
+{% note info %}
+
+Please note escaping of special characters in regular expressions. Be sure to use the second slash, since all the standard string literals in SQL can accept C-escaped strings, and the `\d` sequence is not valid sequence (even if it were, it wouldn't search for numbers as intended).
+
+{% endnote %}
+
+You can enable the case-insensitive mode by specifying, at the beginning of the regular expression, the flag `(?i)`.
+
+## Grep {#grep}
+
+Matches the regular expression with a **part of the string** (arbitrary substring).
+
+## Match {#match}
+
+Matches **the whole string** against the regular expression.
+
+To get a result similar to `Grep` (where substring matching is included), enclose the regular expression in `.*` (`.*foo.*` instead of `foo`). However, in terms of code readability, it's usually better to change the function.
+
+## BacktrackingGrep/BacktrackingMatch {#backtrackinggrep}
+
+The functions are identical to the same-name functions without the `Backtracking` prefix. However, they support a broader range of regular expressions. This is due to the fact that if a specific regular expression is not fully supported by Hyperscan, the library switches to the prefilter mode. In this case, it responds not by "Yes" or "No", but by "Definitely not" or "Maybe yes". The "Maybe yes" responses are then automatically rechecked using a slower, but more functional, library [libpcre](https://www.pcre.org).
+
+## MultiGrep/MultiMatch {#multigrep}
+
+Hyperscan lets you match against multiple regular expressions in a single pass through the text, and get a separate response for each match.
+
+However, if you want to match a string against any of the listed expressions (the results would be joined with "or"), it would be more efficient to combine the query parts in a single regular expression with `|` and match it with regular `Grep` or `Match`.
+
+When you call `MultiGrep`/`MultiMatch`, regular expressions are passed one per line using [multiline string literals](../../syntax/expressions.md#named-nodes):
+
+### Example
+
+```yql
+$multi_match = Hyperscan::MultiMatch(@@a.*
+.*x.*
+.*axa.*@@);
+
+SELECT
+ $multi_match("a") AS a, -- (true, false, false)
+ $multi_match("axa") AS axa; -- (true, true, true)
+```
+
+## Capture and Replace {#capture}
+
+`Hyperscan::Capture` if a string matches the specified regular expression, it returns the last substring matching the regular expression. `Hyperscan::Replace` replaces all occurrences of the specified regular expression with the specified string.
+
+Hyperscan doesn't support advanced functionality for such operations. Although `Hyperscan::Capture` and `Hyperscan::Replace` are implemented for consistency, it's better to use the same-name functions from the Re2 library for any non-trivial capture and replace:
+
+* [Re2::Capture](re2.md#capture);
+* [Re2::Replace](re2.md#replace).
+
+## Usage example
+
+```yql
+$value = "xaaxaaXaa";
+
+$match = Hyperscan::Match("a.*");
+$grep = Hyperscan::Grep("axa");
+$insensitive_grep = Hyperscan::Grep("(?i)axaa$");
+$multi_match = Hyperscan::MultiMatch(@@a.*
+.*a.*
+.*a
+.*axa.*@@);
+
+$capture = Hyperscan::Capture(".*a{2}.*");
+$capture_many = Hyperscan::Capture(".*x(a+).*");
+$replace = Hyperscan::Replace("xa");
+
+SELECT
+ $match($value) AS match, -- false
+ $grep($value) AS grep, -- true
+ $insensitive_grep($value) AS insensitive_grep, -- true
+ $multi_match($value) AS multi_match, -- (false, true, true, true)
+ $multi_match($value).0 AS some_multi_match, -- false
+ $capture($value) AS capture, -- "xaa"
+ $capture_many($value) AS capture_many, -- "xa"
+ $replace($value, "b") AS replace -- "babaXaa"
+;
+```
+
diff --git a/yql/essentials/docs/en/udf/list/index.md b/yql/essentials/docs/en/udf/list/index.md
new file mode 100644
index 00000000000..f89751e80be
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/index.md
@@ -0,0 +1,18 @@
+# Functions of built-in C++ libraries
+
+Many application functions that on the one hand are too specific to become part of the YQL core, and on the other hand might be useful to a wide range of users, are available through built-in C++ libraries.
+
+* [DateTime](datetime.md)
+* [Digest](digest.md)
+* [Histogram](histogram.md)
+* [Hyperscan](hyperscan.md)
+* [Ip](ip.md)
+* [Math](math.md)
+* [Pcre](pcre.md)
+* [Pire](pire.md)
+* [Re2](re2.md)
+* [String](string.md)
+* [Unicode](unicode.md)
+* [Url](url.md)
+* [Yson](yson.md)
+
diff --git a/yql/essentials/docs/en/udf/list/ip.md b/yql/essentials/docs/en/udf/list/ip.md
new file mode 100644
index 00000000000..c635d5009e1
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/ip.md
@@ -0,0 +1,47 @@
+# Ip
+
+The `Ip` module supports both the IPv4 and IPv6 addresses. By default, they are represented as binary strings of 4 and 16 bytes, respectively.
+
+## List of functions
+
+* `Ip::FromString(String{Flags:AutoMap}) -> String?` - From a human-readable representation to a binary representation.
+* `Ip::SubnetFromString(String{Flags:AutoMap}) -> String?` - From a human-readable representation of subnet to a binary representation.
+* `Ip::ToString(String{Flags:AutoMap}) -> String?` - From a binary representation to a human-readable representation.
+* `Ip::SubnetToString(String{Flags:AutoMap}) -> String?` - From a binary representation of subnet to a human-readable representation.
+* `Ip::IsIPv4(String?) -> Bool`
+* `Ip::IsIPv6(String?) -> Bool`
+* `Ip::IsEmbeddedIPv4(String?) -> Bool`
+* `Ip::ConvertToIPv6(String{Flags:AutoMap}) -> String`: IPv6 remains unchanged, and IPv4 becomes embedded in IPv6
+* `Ip::GetSubnet(String{Flags:AutoMap}, [Uint8?]) -> String`: The second argument is the subnet size, by default it's 24 for IPv4 and 64 for IPv6
+* `Ip::GetSubnetByMask(String{Flags:AutoMap}, String{Flags:AutoMap}) -> String`: The first argument is the base address, the second argument is the bit mask of a desired subnet.
+* `Ip::SubnetMatch(String{Flags:AutoMap}, String{Flags:AutoMap}) -> Bool`: The first argument is a subnet, the second argument is a subnet or an address.
+
+
+## Examples
+
+```yql
+SELECT Ip::IsEmbeddedIPv4(
+ Ip::FromString("::ffff:77.75.155.3")
+); -- true
+
+SELECT
+ Ip::ToString(
+ Ip::GetSubnet(
+ Ip::FromString("213.180.193.3")
+ )
+ ); -- "213.180.193.0"
+
+SELECT
+ Ip::SubnetMatch(
+ Ip::SubnetFromString("192.168.0.1/16"),
+ Ip::FromString("192.168.1.14"),
+ ); -- true
+
+SELECT
+ Ip::ToString(
+ Ip::GetSubnetByMask(
+ Ip::FromString("192.168.0.1"),
+ Ip::FromString("255.255.0.0")
+ )
+ ); -- "192.168.0.0"
+```
diff --git a/yql/essentials/docs/en/udf/list/math.md b/yql/essentials/docs/en/udf/list/math.md
new file mode 100644
index 00000000000..dd129399d84
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/math.md
@@ -0,0 +1,140 @@
+# Math
+
+A set of wrappers around the functions from the libm library and the Yandex utilities.
+
+## Constants {#constants}
+
+### List of functions
+
+* `Math::Pi() -> Double`
+* `Math::E() -> Double`
+* `Math::Eps() -> Double`
+
+### Examples
+
+```yql
+SELECT Math::Pi(); -- 3.141592654
+SELECT Math::E(); -- 2.718281828
+SELECT Math::Eps(); -- 2.220446049250313e-16
+```
+
+## (Double) -> Bool {#double-bool}
+
+### List of functions
+
+* `Math::IsInf(Double{Flags:AutoMap}) -> Bool`
+* `Math::IsNaN(Double{Flags:AutoMap}) -> Bool`
+* `Math::IsFinite(Double{Flags:AutoMap}) -> Bool`
+
+### Examples
+
+```yql
+SELECT Math::IsNaN(0.0/0.0); -- true
+SELECT Math::IsFinite(1.0/0.0); -- false
+```
+
+## (Double) -> Double {#double-double}
+
+### List of functions
+
+* `Math::Abs(Double{Flags:AutoMap}) -> Double`
+* `Math::Acos(Double{Flags:AutoMap}) -> Double`
+* `Math::Asin(Double{Flags:AutoMap}) -> Double`
+* `Math::Asinh(Double{Flags:AutoMap}) -> Double`
+* `Math::Atan(Double{Flags:AutoMap}) -> Double`
+* `Math::Cbrt(Double{Flags:AutoMap}) -> Double`
+* `Math::Ceil(Double{Flags:AutoMap}) -> Double`
+* `Math::Cos(Double{Flags:AutoMap}) -> Double`
+* `Math::Cosh(Double{Flags:AutoMap}) -> Double`
+* `Math::Erf(Double{Flags:AutoMap}) -> Double`
+* `Math::ErfInv(Double{Flags:AutoMap}) -> Double`
+* `Math::ErfcInv(Double{Flags:AutoMap}) -> Double`
+* `Math::Exp(Double{Flags:AutoMap}) -> Double`
+* `Math::Exp2(Double{Flags:AutoMap}) -> Double`
+* `Math::Fabs(Double{Flags:AutoMap}) -> Double`
+* `Math::Floor(Double{Flags:AutoMap}) -> Double`
+* `Math::Lgamma(Double{Flags:AutoMap}) -> Double`
+* `Math::Rint(Double{Flags:AutoMap}) -> Double`
+* `Math::Sigmoid(Double{Flags:AutoMap}) -> Double`
+* `Math::Sin(Double{Flags:AutoMap}) -> Double`
+* `Math::Sinh(Double{Flags:AutoMap}) -> Double`
+* `Math::Sqrt(Double{Flags:AutoMap}) -> Double`
+* `Math::Tan(Double{Flags:AutoMap}) -> Double`
+* `Math::Tanh(Double{Flags:AutoMap}) -> Double`
+* `Math::Tgamma(Double{Flags:AutoMap}) -> Double`
+* `Math::Trunc(Double{Flags:AutoMap}) -> Double`
+* `Math::Log(Double{Flags:AutoMap}) -> Double`
+* `Math::Log2(Double{Flags:AutoMap}) -> Double`
+* `Math::Log10(Double{Flags:AutoMap}) -> Double`
+
+### Examples
+
+```yql
+SELECT Math::Sqrt(256); -- 16
+SELECT Math::Trunc(1.2345); -- 1
+```
+
+## (Double, Double) -> Double {#doubledouble-double}
+
+### List of functions
+
+* `Math::Atan2(Double{Flags:AutoMap}, Double{Flags:AutoMap}) -> Double`
+* `Math::Fmod(Double{Flags:AutoMap}, Double{Flags:AutoMap}) -> Double`
+* `Math::Hypot(Double{Flags:AutoMap}, Double{Flags:AutoMap}) -> Double`
+* `Math::Pow(Double{Flags:AutoMap}, Double{Flags:AutoMap}) -> Double`
+* `Math::Remainder(Double{Flags:AutoMap}, Double{Flags:AutoMap}) -> Double`
+
+### Examples
+
+```yql
+SELECT Math::Atan2(1, 0); -- 1.570796327
+SELECT Math::Remainder(2.1, 2); -- 0.1
+```
+
+## (Double, Int32) -> Double {#doubleint32-double}
+
+### List of functions
+
+* `Math::Ldexp(Double{Flags:AutoMap}, Int32{Flags:AutoMap}) -> Double`
+* `Math::Round(Double{Flags:AutoMap}, [Int32?]) -> Double`: The second argument indicates the power of 10 to which we round (it's negative for decimal digits and positive for rounding to tens, thousands, or millions); the default value is 0
+
+### Examples
+
+```yql
+SELECT Math::Pow(2, 10); -- 1024
+SELECT Math::Round(1.2345, -2); -- 1.23
+```
+
+## (Double, Double, \[Double?\]) -> Bool {#doubledouble-bool}
+
+### List of functions
+
+* `Math::FuzzyEquals(Double{Flags:AutoMap}, Double{Flags:AutoMap}, [Double?]) -> Bool`: Compares two Doubles for being inside the neighborhood specified by the third argument; the default value is 1.0e-13
+
+### Examples
+
+```yql
+SELECT Math::FuzzyEquals(1.01, 1.0, 0.05); -- true
+```
+
+## Functions for computing remainders
+
+### List of functions
+
+* `Math::Mod(Int64{Flags:AutoMap}, Int64) -> Int64?`
+* `Math::Rem(Int64{Flags:AutoMap}, Int64) -> Int64?`
+
+These functions behave similarly to the built-in % operator in the case of non-negative arguments. The differences are noticeable in the case of negative arguments:
+
+* `Math::Mod` preserves the sign of the second argument (the denominator).
+* `Math::Rem` preserves the sign of the first argument (the numerator).
+
+Functions return null if the divisor is zero.
+
+### Examples
+
+```yql
+SELECT Math::Mod(-1, 7); -- 6
+SELECT Math::Rem(-1, 7); -- -1
+```
+
diff --git a/yql/essentials/docs/en/udf/list/pcre.md b/yql/essentials/docs/en/udf/list/pcre.md
new file mode 100644
index 00000000000..9cd2332cefa
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/pcre.md
@@ -0,0 +1,22 @@
+# Pcre
+
+The Pcre library is currently an alias to [Hyperscan](hyperscan.md).
+
+If you depend on some features of a certain regular expression engine, it's better to internally implement UDF with a given library inside. Use Pcre as the current most-recommended option for simple matching (that can be potentially changed to something else).
+
+Currently available engines:
+
+* [Hyperscan](hyperscan.md) <span style="color: gray;">(Intel)</span>
+* [Pire](pire.md) <span style="color: gray;">(Yandex)</span>
+* [Re2](re2.md) <span style="color: gray;">(Google)</span>
+
+All three modules provide approximately the same set of functions with an identical interface. This lets you switch between them with minimal changes to a query.
+
+Inside Hyperscan, there are several implementations that use different sets of processor instructions, with the relevant instruction automatically selected based on the current processor. In HyperScan, some functions support backtracking (referencing the previously found part of the string). Those functions are implemented through hybrid use of the two libraries: Hyperscan and libpcre.
+
+[Pire](https://github.com/yandex/pire) (Perl Incompatible Regular Expressions) is a very fast library of regular expressions developed by Yandex. At the lower level, it scans the input string once, without any lookaheads or rollbacks, spending 5 machine instructions per character (on x86 and x86_64). However, since the library almost hasn't been developed since 2011-2013 and its name says "Perl incompatible", you may need to adapt your regular expressions a bit.
+
+Hyperscan and Pire are best-suited for Grep and Match.
+
+The Re2 module uses [google::RE2](https://github.com/google/re2) that offers a wide range of features ([see the official documentation](https://github.com/google/re2/wiki/Syntax)). The main benefit of the Re2 is its advanced Capture and Replace functionality. Use this library, if you need those functions.
+
diff --git a/yql/essentials/docs/en/udf/list/pire.md b/yql/essentials/docs/en/udf/list/pire.md
new file mode 100644
index 00000000000..3776e74c04a
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/pire.md
@@ -0,0 +1,115 @@
+# Pire
+
+## List of functions
+
+* `Pire::Grep(pattern:String) -> (string:String?) -> Bool`
+* `Pire::Match(pattern:String) -> (string:String?) -> Bool`
+* `Pire::MultiGrep(pattern:String) -> (string:String?) -> Tuple<Bool, Bool, ...>`
+* `Pire::MultiMatch(pattern:String) -> (string:String?) -> Tuple<Bool, Bool, ...>`
+* `Pire::Capture(pattern:String) -> (string:String?) -> String?`
+* `Pire::Replace(pattern:String) -> (string:String?, replacement:String) -> String?`
+
+One of the options to match regular expressions in YQL is to use [Pire](https://github.com/yandex/pire) (Perl Incompatible Regular Expressions). This is a very fast library of regular expressions developed at Yandex: at the lower level, it looks up the input string once, without any lookaheads or rollbacks, spending 5 machine instructions per character (on x86 and x86_64).
+
+The speed is achieved by using the reasonable restrictions:
+
+* Pire is primarily focused at checking whether a string matches a regular expression.
+* The matching substring can also be returned (by Capture), but with restrictions (a match with only one group is returned).
+
+By default, all functions work in the single-byte mode. However, if the regular expression is a valid UTF-8 string but is not a valid ASCII string, the UTF-8 mode is enabled automatically.
+
+To enable the Unicode mode, you can put one character that's beyond ASCII with the `?` operator, for example: `\\w+я?`.
+
+## Call syntax {#call-syntax}
+
+To avoid compiling a regular expression at each table row, wrap the function call by [a named expression](../../syntax/expressions.md#named-nodes):
+
+```yql
+$re = Pire::Grep("\\d+"); -- create a callable value to match a specific regular expression
+SELECT * FROM table WHERE $re(key); -- use it to filter the table
+```
+
+{% note alert %}
+
+When escaping special characters in a regular expression, be sure to use the second slash, since all the standard string literals in SQL can accept C-escaped strings, and the `\d` sequence is not a valid sequence (even if it were, it wouldn't search for numbers as intended).
+
+{% endnote %}
+
+You can enable the case-insensitive mode by specifying, at the beginning of the regular expression, the flag `(?i)`.
+
+### Examples
+
+```yql
+$value = "xaaxaaxaa";
+$match = Pire::Match("a.*");
+$grep = Pire::Grep("axa");
+$insensitive_grep = Pire::Grep("(?i)axa");
+$multi_match = Pire::MultiMatch(@@a.*
+.*a.*
+.*a
+.*axa.*@@);
+$capture = Pire::Capture(".*x(a).*");
+$capture_many = Pire::Capture(".*x(a+).*");
+$replace = Pire::Replace(".*x(a).*");
+
+SELECT
+ $match($value) AS match, -- false
+ $grep($value) AS grep, -- true
+ $insensitive_grep($value) AS insensitive_grep, -- true
+ $multi_match($value) AS multi_match, -- (false, true, true, true)
+ $multi_match($value).0 AS some_multi_match, -- false
+ $capture($value) AS capture, -- "a"
+ $capture_many($value) AS capture_many, -- "aa"
+ $replace($value, "b") AS replace; -- "xaaxaaxba"
+```
+
+## Grep {#grep}
+
+Matches the regular expression with a **part of the string** (arbitrary substring).
+
+## Match {#match}
+
+Matches **the whole string** against the regular expression.
+To get a result similar to `Grep` (where substring matching is included), enclose the regular expression in `.*`. For example, use `.*foo.*` instead of `foo`.
+
+## MultiGrep/MultiMatch {#multigrep}
+
+Pire lets you match against multiple regular expressions in a single pass through the text and get a separate response for each match.
+Use the MultiGrep/MultiMatch functions to optimize the query execution speed. Be sure to do it carefully, since the size of the state machine used for matching grows exponentially with the number of regular expressions:
+
+* If you want to match a string against any of the listed expressions (the results are joined with "or"), it would be much more efficient to combine the query parts in a single regular expression with `|` and match it using regular Grep or Match.
+* Pire has a limit on the size of the state machine (YQL uses the default value set in the library). If you exceed the limit, the error is raised at the start of the query: `Failed to glue up regexes, probably the finite state machine appeared to be too large`.
+
+When you call MultiGrep/MultiMatch, regular expressions are passed one per line using [multiline string literals](../../syntax/expressions.md#multiline-string-literals):
+
+### Examples
+
+```yql
+$multi_match = Pire::MultiMatch(@@a.*
+.*x.*
+.*axa.*@@);
+
+SELECT
+ $multi_match("a") AS a, -- (true, false, false)
+ $multi_match("axa") AS axa; -- (true, true, true)
+```
+
+## Capture {#capture}
+
+If a string matches the specified regular expression, it returns a substring that matches the group enclosed in parentheses in the regular expression.
+Capture is non-greedy: the shortest possible substring is returned.
+
+{% note alert %}
+
+The expression must contain only **one** group in parentheses. `NULL` (empty Optional) is returned in case of no match.
+
+{% endnote %}
+
+If the above limitations and features are unacceptable for some reason, we recommend that you consider [Re2::Capture](re2.md#capture).
+
+## REPLACE {#replace}
+
+Pire doesn't support replace based on a regular expression. `Pire::Replace` implemented in YQL is a simplified emulation using `Capture`. It may run correctly, if the substring occurs more than once in the source string.
+
+As a rule, it's better to use [Re2::Replace](re2.md#replace) instead.
+
diff --git a/yql/essentials/docs/en/udf/list/re2.md b/yql/essentials/docs/en/udf/list/re2.md
new file mode 100644
index 00000000000..ab527b012a0
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/re2.md
@@ -0,0 +1,122 @@
+# Re2
+
+## List of functions
+
+```yql
+Re2::Grep(pattern:String, options:Struct<...>?) -> (string:String?) -> Bool
+Re2::Match(pattern:String, options:Struct<...>?) -> (string:String?) -> Bool
+Re2::Capture(pattern:String, options:Struct<...>?) -> (string:String?) -> Struct<_1:String?,foo:String?,...>
+Re2::FindAndConsume(pattern:String, options:Struct<...>?) -> (string:String?) -> List<String>
+Re2::Replace(pattern:String, options:Struct<...>?) -> (string:String?, replacement:String) -> String?
+Re2::Count(pattern:String, options:Struct<...>?) -> (string:String?) -> Uint32
+Re2::Options([CaseSensitive:Bool?,DotNl:Bool?,Literal:Bool?,LogErrors:Bool?,LongestMatch:Bool?,MaxMem:Uint64?,NeverCapture:Bool?,NeverNl:Bool?,OneLine:Bool?,PerlClasses:Bool?,PosixSyntax:Bool?,Utf8:Bool?,WordBoundary:Bool?]) -> Struct<CaseSensitive:Bool,DotNl:Bool,Literal:Bool,LogErrors:Bool,LongestMatch:Bool,MaxMem:Uint64,NeverCapture:Bool,NeverNl:Bool,OneLine:Bool,PerlClasses:Bool,PosixSyntax:Bool,Utf8:Bool,WordBoundary:Bool>
+```
+
+The Re2 module supports regular expressions based on [google::RE2](https://github.com/google/re2) with a wide range of features provided ([see the official documentation](https://github.com/google/re2/wiki/Syntax)).
+
+By default, the UTF-8 mode is enabled automatically if the regular expression is a valid UTF-8-encoded string, but is not a valid ASCII string. You can manually control the settings of the re2 library, if you pass the result of the `Re2::Options` function as the second argument to other module functions, next to the regular expression.
+
+{% note warning %}
+
+Make sure to double all the backslashes in your regular expressions (if they are within a quoted string): standard string literals are treated as C-escaped strings in SQL. You can also format regular expressions as raw strings `@@regexp@@`: double slashes are not needed in this case.
+
+{% endnote %}
+
+## Examples
+
+```yql
+$value = "xaaxaaxaa";
+$options = Re2::Options(false AS CaseSensitive);
+$match = Re2::Match("[ax]+\\d");
+$grep = Re2::Grep("a.*");
+$capture = Re2::Capture(".*(?P<foo>xa?)(a{2,}).*");
+$replace = Re2::Replace("x(a+)x");
+$count = Re2::Count("a", $options);
+
+SELECT
+ $match($value) AS match, -- false
+ $grep($value) AS grep, -- true
+ $capture($value) AS capture, -- (_0: 'xaaxaaxaa', _1: 'aa', foo: 'x')
+ $capture($value)._1 AS capture_member, -- "aa"
+ $replace($value, "b\\1z") AS replace, -- "baazaaxaa"
+ $count($value) AS count; -- 6
+```
+
+## Re2::Grep / Re2::Match {#match}
+
+If you leave out the details of implementation and syntax of regular expressions, those functions are totally similar [to the same-name functions](pire.md#match) from the Pire module. With other things equal and no specific preferences, we recommend that you use `Pire::Grep or Pire::Match`.
+
+You can call the `Re2::Grep` function by using a `REGEXP` expression (see the [basic expression syntax](../../syntax/expressions.md#regexp)).
+
+For example, the following two queries are equivalent (also in terms of computing efficiency):
+
+* `$grep = Re2::Grep("b+"); SELECT $grep("aaabccc");`
+* `SELECT "aaabccc" REGEXP "b+";`
+
+## Re2::Capture {#capture}
+
+Unlike [Pire::Capture](pire.md#capture), `Re2::Capture` supports multiple and named capturing groups.
+Result type: a structure with the fields of the type `String?`.
+
+* Each field corresponds to a capturing group with the applicable name.
+* For unnamed groups, the following names are generated: `_1`, `_2`, etc.
+* The result always includes the `_0` field containing the entire substring matching the regular expression.
+
+For more information about working with structures in YQL, see the [section on containers](../../types/containers.md).
+
+## Re2::FindAndConsume {#findandconsume}
+
+Searches for all occurrences of the regular expression in the passed text and returns a list of values corresponding to the parenthesized part of the regular expression for each occurrence.
+
+## Re2::Replace {#replace}
+
+Works as follows:
+
+* In the input string (first argument), all the non-overlapping substrings matching the regular expression are replaced by the specified string (second argument).
+* In the replacement string, you can use the contents of capturing groups from the regular expression using back-references in the format: `\\1`, `\\2` etc. The `\\0` back-reference stands for the whole substring that matches the regular expression.
+
+## Re2::Count {#count}
+
+Returns the number of non-overlapping substrings of the input string that have matched the regular expression.
+
+## Re2::Options {#options}
+
+Notes on Re2::Options from the official [repository](https://github.com/google/re2/blob/main/re2/re2.h#L595-L617)
+
+| Parameter | Default | Comments |
+| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------- |
+| CaseSensitive:Bool? | true | match is case-sensitive (regexp can override with (?i) unless in posix_syntax mode) |
+| DotNl:Bool? | false | let `.` match `\n` (default ) |
+| Literal:Bool? | false | interpret string as literal, not regexp |
+| LogErrors:Bool? | true | log syntax and execution errors to ERROR |
+| LongestMatch:Bool? | false | search for longest match, not first match |
+| MaxMem:Uint64? | - | (see below) approx. max memory footprint of RE2 |
+| NeverCapture:Bool? | false | parse all parents as non-capturing |
+| NeverNl:Bool? | false | never match \n, even if it is in regexp |
+| PosixSyntax:Bool? | false | restrict regexps to POSIX egrep syntax |
+| Utf8:Bool? | true | text and pattern are UTF-8; otherwise Latin-1 |
+| The following options are only consulted when PosixSyntax == true. <bt>When PosixSyntax == false, these features are always enabled and cannot be turned off; to perform multi-line matching in that case, begin the regexp with (?m). |
+| PerlClasses:Bool? | false | allow Perl's \d \s \w \D \S \W |
+| WordBoundary:Bool? | false | allow Perl's \b \B (word boundary and not) |
+| OneLine:Bool? | false | ^ and $ only match beginning and end of text |
+
+It is not recommended to use Re2::Options in the code. Most parameters can be replaced with regular expression flags.
+
+### Flag usage examples
+
+```yql
+$value = "Foo bar FOO"u;
+-- enable case-insensitive mode
+$capture = Re2::Capture(@@(?i)(foo)@@);
+
+SELECT
+ $capture($value) AS capture; -- ("_0": "Foo", "_1": "Foo")
+
+$capture = Re2::Capture(@@(?i)(?P<foo>FOO).*(?P<bar>bar)@@);
+
+SELECT
+ $capture($value) AS capture; -- ("_0": "Foo bar", "bar": "bar", "foo": "Foo")
+```
+
+In both cases, the word FOO will be found. Using the raw string @@regexp@@ lets you avoid double slashes.
+
diff --git a/yql/essentials/docs/en/udf/list/string.md b/yql/essentials/docs/en/udf/list/string.md
new file mode 100644
index 00000000000..73281c25148
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/string.md
@@ -0,0 +1,148 @@
+# String
+
+Functions for ASCII strings:
+
+## List of functions
+
+* `String::Base64Encode(String{Flags:AutoMap}) -> String`
+
+* `String::Base64Decode(String) -> String?`
+
+* `String::Base64StrictDecode(String) -> String?`
+
+* `String::EscapeC(String{Flags:AutoMap}) -> String`
+
+* `String::UnescapeC(String{Flags:AutoMap}) -> String`
+
+* `String::HexEncode(String{Flags:AutoMap}) -> String`
+
+* `String::HexDecode(String) -> String?`
+
+* `String::EncodeHtml(String{Flags:AutoMap}) -> String`
+
+* `String::DecodeHtml(String{Flags:AutoMap}) -> String`
+
+* `String::CgiEscape(String{Flags:AutoMap}) -> String`
+
+* `String::CgiUnescape(String{Flags:AutoMap}) -> String`
+
+* `String::Strip(String{Flags:AutoMap}) -> String`
+
+* `String::Collapse(String{Flags:AutoMap}) -> String`
+
+* `String::CollapseText(String{Flags:AutoMap}, Uint64) -> String`
+
+* `String::Contains(String?, String) -> Bool`
+
+* `String::Find(String{Flags:AutoMap}, String, [Uint64?]) -> Int64`: Returns the first position found or -1. The optional argument is the offset from the beginning of the string.
+
+* `String::ReverseFind(String{Flags:AutoMap}, String, [Uint64?]) -> Int64`: Returns the last position found or -1. The optional argument is the offset from the beginning of the string.
+
+* `String::HasPrefix(String?, String) -> Bool`
+
+* `String::HasPrefixIgnoreCase(String?, String) -> Bool`
+
+* `String::StartsWith(String?, String) -> Bool`
+
+* `String::StartsWithIgnoreCase(String?, String) -> Bool`
+
+* `String::HasSuffix(String?, String) -> Bool`
+
+* `String::HasSuffixIgnoreCase(String?, String) -> Bool`
+
+* `String::EndsWith(String?, String) -> Bool`
+
+* `String::EndsWithIgnoreCase(String?, String) -> Bool`
+
+* `String::Substring(String{Flags:AutoMap}, [Uint64?, Uint64?]) -> String`
+
+* `String::AsciiToLower(String{Flags:AutoMap}) -> String`: Changes only Latin characters. For working with other alphabets, see Unicode::ToLower
+
+* `String::AsciiToUpper(String{Flags:AutoMap}) -> String`: Changes only Latin characters. For working with other alphabets, see Unicode::ToUpper
+
+* `String::AsciiToTitle(String{Flags:AutoMap}) -> String`: Changes only Latin characters. For working with other alphabets, see Unicode::ToTitle
+
+* `String::SplitToList( String?, String, [ DelimeterString:Bool?, SkipEmpty:Bool?, Limit:Uint64? ]) -> List<String>`
+
+ The first argument is the source string
+ The second argument is a delimiter
+ The third argument includes the following parameters:
+
+ - DelimeterString:Bool? — treating a delimiter as a string (true, by default) or a set of characters "any of" (false)
+ - SkipEmpty:Bool? — whether to skip empty strings in the result, is false by default
+ - Limit:Uint64? — Limits the number of fetched components (unlimited by default); if the limit is exceeded, the raw suffix of the source string is returned in the last item
+
+* `String::JoinFromList(List<String>{Flags:AutoMap}, String) -> String`
+
+* `String::ToByteList(List<String>{Flags:AutoMap}) -> List<Byte>`
+
+* `String::FromByteList(List<Uint8>) -> String`
+
+* `String::ReplaceAll(String{Flags:AutoMap}, String, String) -> String`: Arguments: input, find, replacement
+
+* `String::ReplaceFirst(String{Flags:AutoMap}, String, String) -> String`: Arguments: input, find, replacement
+
+* `String::ReplaceLast(String{Flags:AutoMap}, String, String) -> String`: Arguments: input, find, replacement
+
+* `String::RemoveAll(String{Flags:AutoMap}, String) -> String`: The second argument is interpreted as an unordered set of characters to delete
+
+* `String::RemoveFirst(String{Flags:AutoMap}, String) -> String`: An unordered set of characters in the second argument, only the first encountered character from set is deleted
+
+* `String::RemoveLast(String{Flags:AutoMap}, String) -> String`: An unordered set of characters in the second argument, only the last encountered character from the set is deleted
+
+* `String::IsAscii(String{Flags:AutoMap}) -> Bool`
+
+* `String::IsAsciiSpace(String{Flags:AutoMap}) -> Bool`
+
+* `String::IsAsciiUpper(String{Flags:AutoMap}) -> Bool`
+
+* `String::IsAsciiLower(String{Flags:AutoMap}) -> Bool`
+
+* `String::IsAsciiAlpha(String{Flags:AutoMap}) -> Bool`
+
+* `String::IsAsciiAlnum(String{Flags:AutoMap}) -> Bool`
+
+* `String::IsAsciiHex(String{Flags:AutoMap}) -> Bool`
+
+* `String::LevensteinDistance(String{Flags:AutoMap}, String{Flags:AutoMap}) -> Uint64`
+
+* `String::LeftPad(String{Flags:AutoMap}, Uint64, [String?]) -> String`
+
+* `String::RightPad(String{Flags:AutoMap}, Uint64) -> String`
+
+* `String::Hex(Uint64{Flags:AutoMap}) -> String`
+
+* `String::SHex(Int64{Flags:AutoMap}) -> String`
+
+* `String::Bin(Uint64{Flags:AutoMap}) -> String`
+
+* `String::SBin(Int64{Flags:AutoMap}) -> String`
+
+* `String::HexText(String{Flags:AutoMap}) -> String`
+
+* `String::BinText(String{Flags:AutoMap}) -> String`
+
+* `String::HumanReadableDuration(Uint64{Flags:AutoMap}) -> String`
+
+* `String::HumanReadableQuantity(Uint64{Flags:AutoMap}) -> String`
+
+* `String::HumanReadableBytes(Uint64{Flags:AutoMap}) -> String`
+
+* `String::Prec(Double{Flags:AutoMap}, Uint64) -> String`
+
+* `String::Reverse(String?) -> String?`
+
+{% note alert %}
+
+The functions from the String library don't support Cyrillic and can only work with ASCII characters. To work with UTF-8 encoded strings, use functions from [Unicode](unicode.md).
+
+{% endnote %}
+
+## Examples
+
+```yql
+SELECT String::Base64Encode("YQL"); -- "WVFM"
+SELECT String::Strip("YQL "); -- "YQL"
+SELECT String::SplitToList("1,2,3,4,5,6,7", ",", 3 as Limit); -- ["1", "2", "3", "4,5,6,7"]
+```
+
diff --git a/yql/essentials/docs/en/udf/list/toc_base.yaml b/yql/essentials/docs/en/udf/list/toc_base.yaml
new file mode 100644
index 00000000000..8dbeb3fb827
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/toc_base.yaml
@@ -0,0 +1,16 @@
+items:
+- name: Overview
+ href: index.md
+- { name: DateTime, href: datetime.md }
+- { name: Digest, href: digest.md }
+- { name: Histogram, href: histogram.md }
+- { name: Hyperscan, href: hyperscan.md }
+- { name: Ip, href: ip.md }
+- { name: Math, href: math.md }
+- { name: Pcre, href: pcre.md }
+- { name: Pire, href: pire.md }
+- { name: Re2, href: re2.md }
+- { name: String, href: string.md }
+- { name: Unicode, href: unicode.md }
+- { name: Url, href: url.md }
+- { name: Yson, href: yson.md }
diff --git a/yql/essentials/docs/en/udf/list/toc_i.yaml b/yql/essentials/docs/en/udf/list/toc_i.yaml
new file mode 100644
index 00000000000..28325999d2f
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/toc_i.yaml
@@ -0,0 +1,2 @@
+items:
+- include: { mode: link, path: toc_base.yaml } \ No newline at end of file
diff --git a/yql/essentials/docs/en/udf/list/unicode.md b/yql/essentials/docs/en/udf/list/unicode.md
new file mode 100644
index 00000000000..7509daa23c5
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/unicode.md
@@ -0,0 +1,159 @@
+# Unicode
+
+Functions for Unicode strings.
+
+## List of functions
+
+* `Unicode::IsUtf(String) -> Bool`
+
+ Checks whether a string is a valid UTF-8 sequence. For example, the string `"\xF0"` isn't a valid UTF-8 sequence, but the string `"\xF0\x9F\x90\xB1"` correctly describes a UTF-8 cat emoji.
+
+* `Unicode::GetLength(Utf8{Flags:AutoMap}) -> Uint64`
+
+ Returns the length of a utf-8 string in unicode code points. Surrogate pairs are counted as one character.
+
+ ```yql
+ SELECT Unicode::GetLength("жніўня"); -- 6
+ ```
+
+* `Unicode::Find(string:Utf8{Flags:AutoMap}, subString:Utf8, [pos:Uint64?]) -> Uint64?`
+
+* `Unicode::RFind(string:Utf8{Flags:AutoMap}, subString:Utf8, [pos:Uint64?]) -> Uint64?`
+
+ Finding the first (`RFind` - the last) occurrence of a substring in a string starting from the `pos` position. Returns the position of the first character from the found substring. In case of failure, returns Null.
+
+ ```yql
+ SELECT Unicode::Find("aaa", "bb"); -- Null
+ ```
+
+* `Unicode::Substring(string:Utf8{Flags:AutoMap}, from:Uint64?, len:Uint64?) -> Utf8`
+
+ Returns a `string` substring starting with `from` that is `len` characters long. If the `len` argument is omitted, the substring is taken to the end of the source string.
+
+ If `from` exceeds the length of the original string, an empty string `""` is returned.
+
+ ```yql
+ SELECT Unicode::Substring("0123456789abcdefghij", 10); -- "abcdefghij"
+ ```
+
+* The `Unicode::Normalize...` functions convert the passed UTF-8 string to a [normalization form](https://unicode.org/reports/tr15/#Norm_Forms):
+
+ * `Unicode::Normalize(Utf8{Flags:AutoMap}) -> Utf8` -- NFC
+ * `Unicode::NormalizeNFD(Utf8{Flags:AutoMap}) -> Utf8`
+ * `Unicode::NormalizeNFC(Utf8{Flags:AutoMap}) -> Utf8`
+ * `Unicode::NormalizeNFKD(Utf8{Flags:AutoMap}) -> Utf8`
+ * `Unicode::NormalizeNFKC(Utf8{Flags:AutoMap}) -> Utf8`
+
+* `Unicode::Translit(string:Utf8{Flags:AutoMap}, [lang:String?]) -> Utf8`
+
+ Transliterates with Latin letters the words from the passed string, consisting entirely of characters of the alphabet of the language passed by the second argument. If no language is specified, the words are transliterated from Russian. Available languages: "kaz", "rus", "tur", and "ukr".
+
+ ```yql
+ SELECT Unicode::Translit("Тот уголок земли, где я провел"); -- "Tot ugolok zemli, gde ya provel"
+ ```
+
+* `Unicode::LevensteinDistance(stringA:Utf8{Flags:AutoMap}, stringB:Utf8{Flags:AutoMap}) -> Uint64`
+
+ Calculates the Levenshtein distance for the passed strings.
+
+* `Unicode::Fold(Utf8{Flags:AutoMap}, [ Language:String?, DoLowerCase:Bool?, DoRenyxa:Bool?, DoSimpleCyr:Bool?, FillOffset:Bool? ]) -> Utf8`
+
+ Performs [case folding](https://www.w3.org/TR/charmod-norm/#definitionCaseFolding) on the passed string.
+
+ Parameters:
+
+ - `Language` is set according to the same rules as in `Unicode::Translit()`.
+ - `DoLowerCase` converts a string to lowercase letters, defaults to `true`.
+ - `DoRenyxa` converts diacritical characters to similar Latin characters, defaults to `true`.
+ - `DoSimpleCyr` converts diacritical Cyrillic characters to similar Latin characters, defaults to `true`.
+ - `FillOffset` parameter is not used.
+
+ ```yql
+ SELECT Unicode::Fold("Kongreßstraße", false AS DoSimpleCyr, false AS DoRenyxa); -- "kongressstrasse"
+ SELECT Unicode::Fold("ҫурт"); -- "сурт"
+ SELECT Unicode::Fold("Eylül", "Turkish" AS Language); -- "eylul"
+ ```
+
+* `Unicode::ReplaceAll(input:Utf8{Flags:AutoMap}, find:Utf8, replacement:Utf8) -> Utf8`
+
+* `Unicode::ReplaceFirst(input:Utf8{Flags:AutoMap}, find:Utf8, replacement:Utf8) -> Utf8`
+
+* `Unicode::ReplaceLast(input:Utf8{Flags:AutoMap}, find:Utf8, replacement:Utf8) -> Utf8`
+
+ Replaces all/first/last occurrences of the `find` string in the `input` with `replacement`.
+
+* `Unicode::RemoveAll(input:Utf8{Flags:AutoMap}, symbols:Utf8) -> Utf8`
+
+* `Unicode::RemoveFirst(input:Utf8{Flags:AutoMap}, symbols:Utf8) -> Utf8`
+
+* `Unicode::RemoveLast(input:Utf8{Flags:AutoMap}, symbols:Utf8) -> Utf8`
+
+ Deletes all/first/last occurrences of characters in the `symbols` set from the `input`. The second argument is interpreted as an unordered set of characters to be removed.
+
+ ```yql
+ SELECT Unicode::ReplaceLast("absence", "enc", ""); -- "abse"
+ SELECT Unicode::RemoveAll("abandon", "an"); -- "bdo"
+ ```
+
+* `Unicode::ToCodePointList(Utf8{Flags:AutoMap}) -> List<Uint32>`
+
+ Splits a string into a Unicode sequence of codepoints.
+
+* `Unicode::FromCodePointList(List<Uint32>{Flags:AutoMap}) -> Utf8`
+
+ Generates a Unicode string from codepoints.
+
+ ```yql
+ SELECT Unicode::ToCodePointList("Щавель"); -- [1065, 1072, 1074, 1077, 1083, 1100]
+ SELECT Unicode::FromCodePointList(AsList(99,111,100,101,32,112,111,105,110,116,115,32,99,111,110,118,101,114,116,101,114)); -- "code points converter"
+ ```
+
+* `Unicode::Reverse(Utf8{Flags:AutoMap}) -> Utf8`
+
+ Reverses a string.
+
+* `Unicode::ToLower(Utf8{Flags:AutoMap}) -> Utf8`
+
+* `Unicode::ToUpper(Utf8{Flags:AutoMap}) -> Utf8`
+
+* `Unicode::ToTitle(Utf8{Flags:AutoMap}) -> Utf8`
+
+ Converts a string to UPPER, lower, or Title case.
+
+* `Unicode::SplitToList( string:Utf8?, separator:Utf8, [ DelimeterString:Bool?, SkipEmpty:Bool?, Limit:Uint64? ]) -> List<Utf8>`
+
+ Splits a string into substrings by separator.
+`string` -- Source string. `separator` -- Separator. Parameters:
+
+ - DelimeterString:Bool? — treating a delimiter as a string (true, by default) or a set of characters "any of" (false)
+ - SkipEmpty:Bool? - whether to skip empty strings in the result, is false by default
+ - Limit:Uint64? - Limits the number of fetched components (unlimited by default); if the limit is exceeded, the raw suffix of the source string is returned in the last item
+
+* `Unicode::JoinFromList(List<Utf8>{Flags:AutoMap}, separator:Utf8) -> Utf8`
+
+ Concatenates a list of strings via a `separator` into a single string.
+
+ ```yql
+ SELECT Unicode::SplitToList("One, two, three, four, five", ", ", 2 AS Limit); -- ["One", "two", "three, four, five"]
+ SELECT Unicode::JoinFromList(["One", "two", "three", "four", "five"], ";"); -- "One;two;three;four;five"
+ ```
+
+* `Unicode::ToUint64(string:Utf8{Flags:AutoMap}, [prefix:Uint16?]) -> Uint64`
+
+ Converts a string to a number.
+
+ The second optional argument sets the number system. By default, 0 (automatic detection by prefix).
+ Supported prefixes: `0x(0X)` - base-16, `0` - base-8. Defaults to base-10.
+ The `-` sign before a number is interpreted as in C unsigned arithmetic. For example, `-0x1` -> UI64_MAX.
+ If there are incorrect characters in a string or a number goes beyond ui64, the function terminates with an error.
+
+* `Unicode::TryToUint64(string:Utf8{Flags:AutoMap}, [prefix:Uint16?]) -> Uint64?`
+
+ Similar to the `Unicode::ToUint64()` function, except that it returns `NULL` instead of an error.
+
+ ```yql
+ SELECT Unicode::ToUint64("77741"); -- 77741
+ SELECT Unicode::ToUint64("-77741"); -- 18446744073709473875
+ SELECT Unicode::TryToUint64("asdh831"); -- Null
+ ```
+
diff --git a/yql/essentials/docs/en/udf/list/url.md b/yql/essentials/docs/en/udf/list/url.md
new file mode 100644
index 00000000000..0dbe3d5b48d
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/url.md
@@ -0,0 +1,203 @@
+# Url
+
+## Normalize {#normalize}
+
+* `Url::Normalize(String) -> String?`
+
+Normalizes the URL in a robot-friendly way: converts the hostname into lowercase, strips out certain fragments, and so on.
+The normalization result only depends on the URL itself. The normalization **DOES NOT** include operations depending on the external data: transformation based on duplicates, mirrors, etc.
+
+Returned value:
+
+* Normalized URL.
+* `NULL`, if the passed string argument can't be parsed as a URL.
+
+### Examples
+
+```yql
+SELECT Url::Normalize("hTTp://wWw.yDb.TECH/"); -- "http://www.ydb.tech/"
+SELECT Url::Normalize("http://ydb.tech#foo"); -- "http://ydb.tech/"
+```
+
+## NormalizeWithDefaultHttpScheme {#normalizewithdefaulthttpscheme}
+
+* `Url::NormalizeWithDefaultHttpScheme(String?) -> String?`
+
+Normalizes similarly to `Url::Normalize`, but inserts the `http://` schema in case there is no schema.
+
+Returned value:
+
+* Normalized URL.
+* Source URL, if the normalization has failed.
+
+### Examples
+
+```yql
+SELECT Url::NormalizeWithDefaultHttpScheme("wWw.yDb.TECH"); -- "http://www.ydb.tech/"
+SELECT Url::NormalizeWithDefaultHttpScheme("http://ydb.tech#foo"); -- "http://ydb.tech/"
+```
+
+## Encode / Decode {#encode}
+
+Encode a UTF-8 string to the urlencoded format (`Url::Encode`) and back (`Url::Decode`).
+
+### List of functions
+
+* `Url::Encode(String?) -> String?`
+* `Url::Decode(String?) -> String?`
+
+### Examples
+
+```yql
+SELECT Url::Decode("http://ydb.tech/%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0");
+ -- "http://ydb.tech/page"
+SELECT Url::Encode("http://ydb.tech/page");
+ -- "http://ydb.tech/%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0"
+```
+
+## Parse {#parse}
+
+Parses the URL into parts.
+
+```yql
+Url::Parse(Parse{Flags:AutoMap}) -> Struct< Frag: String?, Host: String?, ParseError: String?, Pass: String?, Path: String?, Port: String?, Query: String?, Scheme: String?, User: String? >
+```
+
+### Examples
+
+```yql
+SELECT Url::Parse(
+ "https://en.wikipedia.org/wiki/Isambard_Kingdom_Brunel?s=24&g=h-24#Great_Western_Railway");
+/*
+(
+ "Frag": "Great_Western_Railway",
+ "Host": "en.wikipedia.org",
+ "ParseError": null,
+ "Pass": null,
+ "Path": "/wiki/Isambard_Kingdom_Brunel",
+ "Port": null,
+ "Query": "s=24&g=h-24",
+ "Scheme": "https",
+ "User": null
+)
+*/
+```
+
+## Get... {#get}
+
+Get a component of the URL.
+
+### List of functions
+
+* `Url::GetScheme(String{Flags:AutoMap}) -> String`
+* `Url::GetHost(String?) -> String?`
+* `Url::GetHostPort(String?) -> String?`
+* `Url::GetSchemeHost(String?) -> String?`
+* `Url::GetSchemeHostPort(String?) -> String?`
+* `Url::GetPort(String?) -> String?`
+* `Url::GetTail(String?) -> String?` -- everything following the host: path + query + fragment
+* `Url::GetPath(String?) -> String?`
+* `Url::GetFragment(String?) -> String?`
+* `Url::GetCGIParam(String?, String) -> String?` -- The second parameter is the name of the intended CGI parameter.
+* `Url::GetDomain(String?, Uint8) -> String?` -- The second parameter is the required domain level.
+* `Url::GetTLD(String{Flags:AutoMap}) -> String`
+* `Url::IsKnownTLD(String{Flags:AutoMap}) -> Bool` -- Registered on [iana.org](http://www.iana.org/).
+* `Url::IsWellKnownTLD(String{Flags:AutoMap}) -> Bool` -- Belongs to a small whitelist of com, net, org, ru, and so on.
+* `Url::GetDomainLevel(String{Flags:AutoMap}) -> Uint64`
+* `Url::GetSignificantDomain(String{Flags:AutoMap}, [List<String>?]) -> String`
+
+ Returns a second-level domain in most cases and a third-level domain for the hostnames like: `***.XXX.YY`, where `XXX` is com, net, org, co, gov, or edu. You can redefine this list using an optional second argument
+
+* `Url::GetOwner(String{Flags:AutoMap}) -> String`
+
+ Returns the domain that's most likely owned by an individual or organization. Unlike `Url::GetSignificantDomain`, it uses a special whitelist. Besides the `***.co.uk` domains, it can return a third-level domain used by free hosting sites and blogs (for example: something.livejournal.com)
+
+### Examples
+
+```yql
+SELECT Url::GetScheme("https://ydb.tech"); -- "https://"
+SELECT Url::GetDomain("http://www.ydb.tech", 2); -- "ydb.tech"
+```
+
+## Cut... {#cut}
+
+* `Url::CutScheme(String?) -> String?`
+
+ Returns the passed URL without the schema (`http://`, `https://`, etc.).
+
+* `Url::CutWWW(String?) -> String?`
+
+ Returns the passed domain without the "www." prefix (if any).
+
+* `Url::CutWWW2(String?) -> String?`
+
+ Returns the passed domain without the prefixes like "www.", "www2.", "wwww777." (if any).
+
+* `Url::CutQueryStringA­ndFragment(String{Flags:AutoMap}) -> String`
+
+ Returns a copy of the passed URL, stripping out all the CGI parameters and fragments ("?foo=bar" and/or "#baz").
+
+### Examples
+
+```yql
+SELECT Url::CutScheme("http://www.ydb.tech"); -- "www.ydb.tech"
+SELECT Url::CutWWW("www.ydb.tech"); -- "ydb.tech"
+```
+
+## ...Punycode... {#punycode}
+
+[Punycode](https://en.wikipedia.org/wiki/Punycode) transformations.
+
+### List of functions
+
+* `Url::HostNameToPunycode(String{Flag:AutoMap}) -> String?`
+* `Url::ForceHostNameToPunycode(String{Flag:AutoMap}) -> String`
+* `Url::PunycodeToHostName(String{Flag:AutoMap}) -> String?`
+* `Url::ForcePunycodeToHostName(String{Flag:AutoMap}) -> String`
+* `Url::CanBePunycodeHostName(String{Flag:AutoMap}) -> Bool`
+
+### Examples
+
+```yql
+SELECT Url::PunycodeToHostName("xn--80aniges7g.xn--j1aef"); -- "example.com"
+```
+
+## ...Query... {#query}
+
+[Query](https://docs.python.org/3/library/urllib.parse.html) transformations.
+
+### List of functions
+
+```yql
+Url::QueryStringToList(String{Flag:AutoMap}, [
+ KeepBlankValues:Bool?, -- Empty values in percent-encoded queries are interpreted as empty strings, defaults to false.
+ Strict:Bool?, -- If false, parsing errors are ignored and incorrect fields are skipped, defaults to true.
+ MaxFields:Uint32?, -- The maximum number of fields. If exceeded, an exception is thrown. Defaults to Max<Uint32>.
+ Separator:String? -- A key-value pair separator, defaults to '&'.
+]) -> List<Tuple<String, String>>
+Url::QueryStringToDict(String{Flag:AutoMap}, [
+ KeepBlankValues:Bool?, -- Empty values in percent-encoded queries are interpreted as empty strings, defaults to false.
+ Strict:Bool?, -- If false, parsing errors are ignored and incorrect fields are skipped, defaults to true.
+ MaxFields:Uint32?, -- The maximum number of fields. If exceeded, an exception is thrown. Defaults to Max<Uint32>.
+ Separator:String? -- A key-value pair separator, defaults to '&'.
+]) -> Dict<String, List<String>>
+Url::BuildQueryString(Dict<String, List<String?>>{Flag:AutoMap}, [
+ Separator:String? -- A key-value pair separator, defaults to '&'.
+]) -> String
+Url::BuildQueryString(Dict<String, String?>{Flag:AutoMap}, [
+ Separator:String? -- A key-value pair separator, defaults to '&'.
+]) -> String
+Url::BuildQueryString(List<Tuple<String, String?>>{Flag:AutoMap}, [
+ Separator:String? -- A key-value pair separator, defaults to '&'.
+]) -> String
+```
+
+### Examples
+
+```yql
+SELECT Url::QueryStringToList("a=1&b=2&a=3"); -- [("a", "1"), ("b", "2"), ("a", "3")]
+SELECT Url::QueryStringToDict("a=1&b=2&a=3"); -- {"b" : ["2"], "a" : ["1", "3"]}
+SELECT Url::BuildQueryString([("a", "1"), ("a", "3"), ("b", "2")]); -- "a=1&a=3&b=2"
+SELECT Url::BuildQueryString({"a" : "1", "b" : "2"}); -- "b=2&a=1"
+SELECT Url::BuildQueryString({"a" : ["1", "3"], "b" : ["2", "4"]}); -- "b=2&b=4&a=1&a=3"
+```
diff --git a/yql/essentials/docs/en/udf/list/yson.md b/yql/essentials/docs/en/udf/list/yson.md
new file mode 100644
index 00000000000..5a398dda9ff
--- /dev/null
+++ b/yql/essentials/docs/en/udf/list/yson.md
@@ -0,0 +1,308 @@
+# Yson
+
+<!-- markdownlint-disable no-trailing-punctuation -->
+
+YSON is a JSON-like data format developed at Yandex.
+
+* Similarities with JSON:
+
+ * Does not have a strict scheme.
+ * Besides simple data types, it supports dictionaries and lists in arbitrary combinations.
+
+* Some differences from JSON:
+
+ * It also has a binary representation in addition to the text representation.
+ * The text representation uses semicolons instead of commas and equal signs instead of colons.
+
+* The concept of "attributes" is supported, that is, named properties that can be assigned to a node in the tree.
+
+Implementation specifics and functionality of the module:
+
+* Along with YSON, this module also supports standard JSON to expand the application scope in a way.
+* It works with a DOM representation of YSON in memory that in YQL terms is passed between functions as a "resource" (see the [description of special data types](../../types/special.md)). Most of the module's functions have the semantics of a query to perform a specified operation with a resource and return an empty [optional](../../types/optional.md) type if the operation failed because the actual data type mismatched the expected one.
+* Provides several main classes of functions (find below a complete list and detailed description of functions):
+
+ * `Yson::Parse***`: Getting a resource with a DOM object from serialized data, with all further operations performed on the obtained resource.
+ * `Yson::From`: Getting a resource with a DOM object from simple YQL data types or containers (lists or dictionaries).
+ * `Yson::ConvertTo***`: Converting a resource to [primitive data types](../../types/primitive.md) or [containers](../../types/containers.md).
+ * `Yson::Lookup***`: Getting a single list item or a dictionary with optional conversion to the relevant data type.
+ * `Yson::YPath***`: Getting one element from the document tree based on the relative path specified, optionally converting it to the relevant data type.
+ * `Yson::Serialize***`: Getting a copy of data from the resource and serializing the data in one of the formats.
+
+* For convenience, when serialized Yson and Json are passed to functions expecting a resource with a DOM object, implicit conversion using `Yson::Parse` or `Yson::ParseJson` is done automatically. In SQL syntax, the dot or square brackets operator automatically adds a `Yson::Lookup` call. To serialize a resource, you still need to call `Yson::ConvertTo***` or `Yson::Serialize***`. It means that, for example, to get the "foo" element as a string from the Yson column named mycolumn and serialized as a dictionary, you can write: `SELECT Yson::ConvertToString(mycolumn["foo"]) FROM mytable;` or `SELECT Yson::ConvertToString(mycolumn.foo) FROM mytable;`. In the variant with a dot, special characters can be escaped by [general rules for IDs](../../syntax/expressions.md#escape).
+
+The module's functions must be considered as "building blocks" from which you can assemble different structures, for example:
+
+* `Yson::Parse*** -> Yson::Serialize***`: Converting from one format to other.
+* `Yson::Parse*** -> Yson::Lookup -> Yson::Serialize***`: Extracting the value of the specified subtree in the source YSON tree.
+* `Yson::Parse*** -> Yson::ConvertToList -> ListMap -> Yson::Lookup***`: Extracting items by a key from the YSON list.
+
+
+
+## Examples
+
+```yql
+$node = Json(@@
+ {"abc": {"def": 123, "ghi": "hello"}}
+@@);
+SELECT Yson::SerializeText($node.abc) AS `yson`;
+-- {"def"=123;"ghi"="\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82"}
+```
+
+```yql
+$node = Yson(@@
+ <a=z;x=y>[
+ {abc=123; def=456};
+ {abc=234; xyz=789};
+ ]
+@@);
+$attrs = Yson::YPath($node, "/@");
+
+SELECT
+ ListMap(Yson::ConvertToList($node), ($x) -> { return Yson::LookupInt64($x, "abc") }) AS abcs,
+ Yson::ConvertToStringDict($attrs) AS attrs,
+ Yson::SerializePretty(Yson::Lookup($node, "7", Yson::Options(false AS Strict))) AS miss;
+
+/*
+- abcs: `[123; 234]`
+- attrs: `{"a"="z";"x"="y"}`
+- miss: `NULL`
+*/
+```
+
+## Yson::Parse... {#ysonparse}
+
+```yql
+Yson::Parse(Yson{Flags:AutoMap}) -> Resource<'Yson2.Node'>
+Yson::ParseJson(Json{Flags:AutoMap}) -> Resource<'Yson2.Node'>
+Yson::ParseJsonDecodeUtf8(Json{Flags:AutoMap}) -> Resource<'Yson2.Node'>
+
+Yson::Parse(String{Flags:AutoMap}) -> Resource<'Yson2.Node'>? -- accepts YSON in any format
+Yson::ParseJson(String{Flags:AutoMap}) -> Resource<'Yson2.Node'>?
+Yson::ParseJsonDecodeUtf8(String{Flags:AutoMap}) -> Resource<'Yson2.Node'>?
+```
+
+The result of all three functions is non-serializable: it can only be passed as the input to other function from the Yson library. However, you can't save it to a table or return to the client as a result of the operation: such an attempt results in a typing error. You also can't return it outside [subqueries](../../syntax/select/index.md): if you need to do this, call [Yson::Serialize](#ysonserialize), and the optimizer will remove unnecessary serialization and deserialization if materialization isn't needed in the end.
+
+{% note info %}
+
+The `Yson::ParseJsonDecodeUtf8` expects that characters outside the ASCII range must be additionally escaped.
+
+{% endnote %}
+
+## Yson::From {#ysonfrom}
+
+```yql
+Yson::From(T) -> Resource<'Yson2.Node'>
+```
+
+`Yson::From` is a polymorphic function that converts most primitive data types and containers (lists, dictionaries, tuples, structures, and so on) into a Yson resource. The source object type must be Yson-compatible. For example, in dictionary keys, you can only use the `String` or `Utf8` data types, but not `String?` or `Utf8?` .
+
+### Example
+
+```yql
+SELECT Yson::Serialize(Yson::From(TableRow())) FROM table1;
+```
+
+## Yson::WithAttributes
+
+```yql
+Yson::WithAttributes(Resource<'Yson2.Node'>{Flags:AutoMap}, Resource<'Yson2.Node'>{Flags:AutoMap}) -> Resource<'Yson2.Node'>?
+```
+
+Adds attributes (the second argument) to the Yson node (the first argument). The attributes must constitute a map node.
+
+## Yson::Equals
+
+```yql
+Yson::Equals(Resource<'Yson2.Node'>{Flags:AutoMap}, Resource<'Yson2.Node'>{Flags:AutoMap}) -> Bool
+```
+
+Checking trees in memory for equality. The operation is tolerant to the source serialization format and the order of keys in dictionaries.
+
+## Yson::GetHash
+
+```yql
+Yson::GetHash(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Uint64
+```
+
+Calculating a 64-bit hash from an object tree.
+
+## Yson::Is...
+
+```yql
+Yson::IsEntity(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
+Yson::IsString(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
+Yson::IsDouble(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
+Yson::IsUint64(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
+Yson::IsInt64(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
+Yson::IsBool(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
+Yson::IsList(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
+Yson::IsDict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
+```
+
+Checking that the current node has the appropriate type. The Entity is `#`.
+
+## Yson::GetLength
+
+```yql
+Yson::GetLength(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Uint64?
+```
+
+Getting the number of elements in a list or dictionary.
+
+## Yson::ConvertTo... {#ysonconvertto}
+
+```yql
+Yson::ConvertTo(Resource<'Yson2.Node'>{Flags:AutoMap}, Type<T>) -> T
+Yson::ConvertToBool(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Bool?
+Yson::ConvertToInt64(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Int64?
+Yson::ConvertToUint64(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Uint64?
+Yson::ConvertToDouble(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Double?
+Yson::ConvertToString(Resource<'Yson2.Node'>{Flags:AutoMap}) -> String?
+Yson::ConvertToList(Resource<'Yson2.Node'>{Flags:AutoMap}) -> List<Resource<'Yson2.Node'>>
+Yson::ConvertToBoolList(Resource<'Yson2.Node'>{Flags:AutoMap}) -> List<Bool>
+Yson::ConvertToInt64List(Resource<'Yson2.Node'>{Flags:AutoMap}) -> List<Int64>
+Yson::ConvertToUint64List(Resource<'Yson2.Node'>{Flags:AutoMap}) -> List<Uint64>
+Yson::ConvertToDoubleList(Resource<'Yson2.Node'>{Flags:AutoMap}) -> List<Double>
+Yson::ConvertToStringList(Resource<'Yson2.Node'>{Flags:AutoMap}) -> List<String>
+Yson::ConvertToDict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,Resource<'Yson2.Node'>>
+Yson::ConvertToBoolDict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,Bool>
+Yson::ConvertToInt64Dict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,Int64>
+Yson::ConvertToUint64Dict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,Uint64>
+Yson::ConvertToDoubleDict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,Double>
+Yson::ConvertToStringDict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,String>
+```
+
+{% note warning %}
+
+These functions do not do implicit type casting by default, that is, the value in the argument must exactly match the function called.
+
+{% endnote %}
+
+`Yson::ConvertTo` is a polymorphic function that converts the data type that is specified in the second argument and supports containers (lists, dictionaries, tuples, structures, and so on) into a Yson resource.
+
+### Example
+
+```yql
+$data = Yson(@@{
+ "name" = "Anya";
+ "age" = 15u;
+ "params" = {
+ "ip" = "95.106.17.32";
+ "last_time_on_site" = 0.5;
+ "region" = 213;
+ "user_agent" = "Mozilla/5.0"
+ }
+}@@);
+SELECT Yson::ConvertTo($data,
+ Struct<
+ name: String,
+ age: Uint32,
+ params: Dict<String,Yson>
+ >
+);
+```
+
+## Yson::Contains {#ysoncontains}
+
+```yql
+Yson::Contains(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Bool?
+```
+
+Checks for a key in the dictionary. If the object type is a map, then it searches among the keys.
+If the object type is a list, then the key must be a decimal number, i.e., an index in the list.
+
+## Yson::Lookup... {#ysonlookup}
+
+```yql
+Yson::Lookup(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Resource<'Yson2.Node'>?
+Yson::LookupBool(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Bool?
+Yson::LookupInt64(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Int64?
+Yson::LookupUint64(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Uint64?
+Yson::LookupDouble(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Double?
+Yson::LookupString(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> String?
+Yson::LookupDict(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Dict<String,Resource<'Yson2.Node'>>?
+Yson::LookupList(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> List<Resource<'Yson2.Node'>>?
+```
+
+The above functions are short notations for a typical use case: `Yson::YPath`: go to a level in the dictionary and then extract the value — `Yson::ConvertTo***`. For all the listed functions, the second argument is a key name from the dictionary (unlike YPath, it has no `/`prefix) or an index from the list (for example, `7`). They simplify the query and produce a small gain in speed.
+
+## Yson::YPath {#ysonypath}
+
+```yql
+Yson::YPath(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Resource<'Yson2.Node'>?
+Yson::YPathBool(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Bool?
+Yson::YPathInt64(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Int64?
+Yson::YPathUint64(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Uint64?
+Yson::YPathDouble(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Double?
+Yson::YPathString(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> String?
+Yson::YPathDict(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Dict<String,Resource<'Yson2.Node'>>?
+Yson::YPathList(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> List<Resource<'Yson2.Node'>>?
+```
+
+Lets you get a part of the resource based on the source resource and the part's path in YPath format.
+
+
+
+## Yson::Attributes {#ysonattributes}
+
+```yql
+Yson::Attributes(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,Resource<'Yson2.Node'>>
+```
+
+Getting all node attributes as a dictionary.
+
+## Yson::Serialize... {#ysonserialize}
+
+```yql
+Yson::Serialize(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Yson -- A binary representation
+Yson::SerializeText(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Yson
+Yson::SerializePretty(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Yson -- To get a text result, wrap it in ToBytes(...)
+```
+
+## Yson::SerializeJson {#ysonserializejson}
+
+```yql
+Yson::SerializeJson(Resource<'Yson2.Node'>{Flags:AutoMap}, [Resource<'Yson2.Options'>?, SkipMapEntity:Bool?, EncodeUtf8:Bool?, WriteNanAsString:Bool?]) -> Json?
+```
+
+* `SkipMapEntity` serializes `#` values in dictionaries. The value of attributes is not affected by the flag. By default, `false`.
+* `EncodeUtf8` responsible for escaping non-ASCII characters. By default, `false`.
+* `WriteNanAsString` allows serializing `NaN` and `Inf` values as strings. By default, `false`.
+
+The `Yson` and `Json` data types returned by serialization functions are special cases of a string that is known to contain data in the given format (Yson/Json).
+
+## Yson::Options {#ysonoptions}
+
+```yql
+Yson::Options([AutoConvert:Bool?, Strict:Bool?]) -> Resource<'Yson2.Options'>
+```
+
+It's passed in the last optional argument (omitted for brevity) to the methods `Parse...`, `ConvertTo...`, `Contains`, `Lookup...`, and `YPath...` that accept the result of the `Yson::Options` call. By default, all the `Yson::Options` fields are false and when enabled (true), they modify the behavior as follows:
+
+* **AutoConvert**: If the value passed to Yson doesn't match the result data type exactly, the value is converted where possible. For example, `Yson::ConvertToInt64` in this mode will convert even Double numbers to Int64.
+* **Strict**: By default, all functions from the Yson library return an error in case of issues during query execution (for example, an attempt to parse a string that is not Yson/Json, or an attempt to search by a key in a scalar type, or when a conversion to an incompatible data type has been requested, and so on). If you disable the strict mode, `NULL` is returned instead of an error in most cases. When converting to a dictionary or list (`ConvertTo<Type>Dict` or `ConvertTo<Type>List`), improper items are excluded from the resulting collection.
+
+### Example
+
+```yql
+$yson = @@{y = true; x = 5.5}@@y;
+SELECT Yson::LookupBool($yson, "z"); --- null
+SELECT Yson::LookupBool($yson, "y"); --- true
+
+SELECT Yson::LookupInt64($yson, "x"); --- Error
+SELECT Yson::LookupInt64($yson, "x", Yson::Options(false as Strict)); --- null
+SELECT Yson::LookupInt64($yson, "x", Yson::Options(true as AutoConvert)); --- 5
+
+SELECT Yson::ConvertToBoolDict($yson); --- Error
+SELECT Yson::ConvertToBoolDict($yson, Yson::Options(false as Strict)); --- { "y": true }
+SELECT Yson::ConvertToDoubleDict($yson, Yson::Options(false as Strict)); --- { "x": 5.5 }
+```
+
+If you need to use the same Yson library settings throughout the query, it's more convenient to use [PRAGMA yson.AutoConvert;](../../syntax/pragma.md#yson.autoconvert) and/or [PRAGMA yson.Strict;](../../syntax/pragma.md#yson.strict). Only with these `PRAGMA` you can affect implicit calls to the Yson library occurring when you work with Yson/Json data types.
+
+## See also
+
+* [{#T}](../../recipes/accessing-json.md)
+* [{#T}](../../recipes/modifying-json.md)