duckdb 0.8.2-dev11.0 → 0.8.2-dev1212.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/binding.gyp +14 -12
- package/binding.gyp.in +1 -1
- package/configure.py +1 -1
- package/duckdb_extension_config.cmake +10 -0
- package/lib/duckdb.d.ts +59 -0
- package/lib/duckdb.js +21 -0
- package/package.json +1 -1
- package/src/duckdb/extension/icu/icu-dateadd.cpp +2 -2
- package/src/duckdb/extension/icu/icu-datefunc.cpp +1 -1
- package/src/duckdb/extension/icu/icu-datepart.cpp +2 -2
- package/src/duckdb/extension/icu/icu-datesub.cpp +2 -2
- package/src/duckdb/extension/icu/icu-datetrunc.cpp +1 -1
- package/src/duckdb/extension/icu/icu-list-range.cpp +1 -1
- package/src/duckdb/extension/icu/icu-makedate.cpp +7 -0
- package/src/duckdb/extension/icu/icu-strptime.cpp +4 -4
- package/src/duckdb/extension/icu/icu-table-range.cpp +5 -5
- package/src/duckdb/extension/icu/icu-timebucket.cpp +16 -16
- package/src/duckdb/extension/icu/icu-timezone.cpp +8 -8
- package/src/duckdb/extension/icu/icu_extension.cpp +5 -7
- package/src/duckdb/extension/json/include/json_common.hpp +47 -231
- package/src/duckdb/extension/json/include/json_executors.hpp +49 -13
- package/src/duckdb/extension/json/include/json_functions.hpp +2 -1
- package/src/duckdb/extension/json/json_common.cpp +272 -40
- package/src/duckdb/extension/json/json_functions/json_structure.cpp +1 -1
- package/src/duckdb/extension/json/json_functions/json_transform.cpp +17 -37
- package/src/duckdb/extension/json/json_functions/json_type.cpp +1 -1
- package/src/duckdb/extension/json/json_functions.cpp +24 -24
- package/src/duckdb/extension/json/json_scan.cpp +3 -6
- package/src/duckdb/extension/parquet/column_reader.cpp +19 -21
- package/src/duckdb/extension/parquet/column_writer.cpp +77 -61
- package/src/duckdb/extension/parquet/include/cast_column_reader.hpp +2 -2
- package/src/duckdb/extension/parquet/include/column_reader.hpp +14 -16
- package/src/duckdb/extension/parquet/include/column_writer.hpp +9 -7
- package/src/duckdb/extension/parquet/include/list_column_reader.hpp +2 -2
- package/src/duckdb/extension/parquet/include/parquet_dbp_decoder.hpp +3 -3
- package/src/duckdb/extension/parquet/include/parquet_decimal_utils.hpp +3 -3
- package/src/duckdb/extension/parquet/include/parquet_file_metadata_cache.hpp +2 -2
- package/src/duckdb/extension/parquet/include/parquet_statistics.hpp +2 -2
- package/src/duckdb/extension/parquet/include/parquet_support.hpp +9 -11
- package/src/duckdb/extension/parquet/include/parquet_writer.hpp +24 -5
- package/src/duckdb/extension/parquet/include/string_column_reader.hpp +1 -1
- package/src/duckdb/extension/parquet/include/struct_column_reader.hpp +2 -3
- package/src/duckdb/extension/parquet/include/zstd_file_system.hpp +2 -2
- package/src/duckdb/extension/parquet/parquet_extension.cpp +191 -19
- package/src/duckdb/extension/parquet/parquet_reader.cpp +6 -6
- package/src/duckdb/extension/parquet/parquet_statistics.cpp +7 -6
- package/src/duckdb/extension/parquet/parquet_writer.cpp +79 -16
- package/src/duckdb/extension/parquet/zstd_file_system.cpp +2 -2
- package/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp +1 -1
- package/src/duckdb/src/catalog/default/default_functions.cpp +16 -0
- package/src/duckdb/src/common/adbc/adbc.cpp +75 -10
- package/src/duckdb/src/common/adbc/driver_manager.cpp +6 -11
- package/src/duckdb/src/common/allocator.cpp +14 -2
- package/src/duckdb/src/common/arrow/arrow_appender.cpp +5 -11
- package/src/duckdb/src/common/arrow/arrow_wrapper.cpp +0 -12
- package/src/duckdb/src/common/assert.cpp +3 -0
- package/src/duckdb/src/common/enum_util.cpp +42 -5
- package/src/duckdb/src/common/enums/logical_operator_type.cpp +4 -0
- package/src/duckdb/src/common/enums/optimizer_type.cpp +2 -0
- package/src/duckdb/src/common/enums/physical_operator_type.cpp +4 -0
- package/src/duckdb/src/common/exception.cpp +2 -2
- package/src/duckdb/src/common/file_system.cpp +15 -0
- package/src/duckdb/src/common/local_file_system.cpp +2 -2
- package/src/duckdb/src/common/multi_file_reader.cpp +181 -18
- package/src/duckdb/src/common/radix_partitioning.cpp +27 -9
- package/src/duckdb/src/common/re2_regex.cpp +1 -1
- package/src/duckdb/src/common/row_operations/row_external.cpp +1 -1
- package/src/duckdb/src/common/sort/merge_sorter.cpp +9 -16
- package/src/duckdb/src/common/sort/partition_state.cpp +44 -11
- package/src/duckdb/src/common/types/batched_data_collection.cpp +7 -2
- package/src/duckdb/src/common/types/column/column_data_allocator.cpp +9 -6
- package/src/duckdb/src/common/types/column/column_data_collection.cpp +17 -2
- package/src/duckdb/src/common/types/column/column_data_collection_segment.cpp +15 -6
- package/src/duckdb/src/common/types/column/partitioned_column_data.cpp +2 -2
- package/src/duckdb/src/common/types/data_chunk.cpp +2 -2
- package/src/duckdb/src/common/types/date.cpp +9 -0
- package/src/duckdb/src/common/types/list_segment.cpp +24 -74
- package/src/duckdb/src/common/types/row/partitioned_tuple_data.cpp +3 -9
- package/src/duckdb/src/common/types/row/tuple_data_collection.cpp +2 -0
- package/src/duckdb/src/common/types/row/tuple_data_scatter_gather.cpp +2 -2
- package/src/duckdb/src/common/types/uuid.cpp +2 -2
- package/src/duckdb/src/common/types/validity_mask.cpp +33 -0
- package/src/duckdb/src/common/types/vector.cpp +15 -14
- package/src/duckdb/src/common/vector_operations/is_distinct_from.cpp +6 -4
- package/src/duckdb/src/core_functions/aggregate/holistic/reservoir_quantile.cpp +2 -0
- package/src/duckdb/src/core_functions/aggregate/nested/list.cpp +2 -2
- package/src/duckdb/src/core_functions/aggregate/regression/regr_avg.cpp +4 -4
- package/src/duckdb/src/core_functions/aggregate/regression/regr_intercept.cpp +4 -4
- package/src/duckdb/src/core_functions/aggregate/regression/regr_r2.cpp +5 -4
- package/src/duckdb/src/core_functions/aggregate/regression/regr_sxx_syy.cpp +8 -8
- package/src/duckdb/src/core_functions/aggregate/regression/regr_sxy.cpp +4 -3
- package/src/duckdb/src/core_functions/function_list.cpp +4 -2
- package/src/duckdb/src/core_functions/scalar/date/date_part.cpp +208 -42
- package/src/duckdb/src/core_functions/scalar/date/epoch.cpp +0 -17
- package/src/duckdb/src/core_functions/scalar/date/make_date.cpp +19 -4
- package/src/duckdb/src/core_functions/scalar/list/list_aggregates.cpp +4 -2
- package/src/duckdb/src/execution/aggregate_hashtable.cpp +34 -18
- package/src/duckdb/src/execution/expression_executor.cpp +1 -1
- package/src/duckdb/src/execution/index/art/art.cpp +149 -139
- package/src/duckdb/src/execution/index/art/fixed_size_allocator.cpp +1 -1
- package/src/duckdb/src/execution/index/art/iterator.cpp +129 -207
- package/src/duckdb/src/execution/index/art/leaf.cpp +8 -37
- package/src/duckdb/src/execution/index/art/node.cpp +113 -120
- package/src/duckdb/src/execution/index/art/node16.cpp +1 -10
- package/src/duckdb/src/execution/index/art/node256.cpp +1 -9
- package/src/duckdb/src/execution/index/art/node4.cpp +12 -13
- package/src/duckdb/src/execution/index/art/node48.cpp +1 -11
- package/src/duckdb/src/execution/index/art/prefix.cpp +228 -350
- package/src/duckdb/src/execution/join_hashtable.cpp +4 -4
- package/src/duckdb/src/execution/operator/aggregate/aggregate_object.cpp +1 -0
- package/src/duckdb/src/execution/operator/aggregate/physical_streaming_window.cpp +8 -3
- package/src/duckdb/src/execution/operator/aggregate/physical_ungrouped_aggregate.cpp +32 -22
- package/src/duckdb/src/execution/operator/aggregate/physical_window.cpp +512 -300
- package/src/duckdb/src/execution/operator/helper/physical_batch_collector.cpp +4 -3
- package/src/duckdb/src/execution/operator/helper/physical_limit.cpp +5 -5
- package/src/duckdb/src/execution/operator/join/physical_asof_join.cpp +414 -283
- package/src/duckdb/src/execution/operator/join/physical_comparison_join.cpp +1 -1
- package/src/duckdb/src/execution/operator/join/physical_hash_join.cpp +21 -10
- package/src/duckdb/src/execution/operator/join/physical_join.cpp +1 -1
- package/src/duckdb/src/execution/operator/join/physical_piecewise_merge_join.cpp +22 -3
- package/src/duckdb/src/execution/operator/join/physical_range_join.cpp +2 -2
- package/src/duckdb/src/execution/operator/persistent/base_csv_reader.cpp +100 -13
- package/src/duckdb/src/execution/operator/persistent/csv_file_handle.cpp +1 -1
- package/src/duckdb/src/execution/operator/persistent/csv_reader_options.cpp +20 -0
- package/src/duckdb/src/execution/operator/persistent/csv_rejects_table.cpp +48 -0
- package/src/duckdb/src/execution/operator/persistent/parallel_csv_reader.cpp +2 -3
- package/src/duckdb/src/execution/operator/persistent/physical_batch_copy_to_file.cpp +6 -4
- package/src/duckdb/src/execution/operator/persistent/physical_batch_insert.cpp +3 -2
- package/src/duckdb/src/execution/operator/persistent/physical_fixed_batch_copy.cpp +3 -3
- package/src/duckdb/src/execution/operator/persistent/physical_insert.cpp +1 -1
- package/src/duckdb/src/execution/operator/projection/physical_pivot.cpp +2 -1
- package/src/duckdb/src/execution/operator/scan/physical_column_data_scan.cpp +19 -0
- package/src/duckdb/src/execution/operator/set/physical_cte.cpp +160 -0
- package/src/duckdb/src/execution/operator/set/physical_recursive_cte.cpp +15 -5
- package/src/duckdb/src/execution/partitionable_hashtable.cpp +41 -6
- package/src/duckdb/src/execution/perfect_aggregate_hashtable.cpp +30 -5
- package/src/duckdb/src/execution/physical_operator.cpp +17 -14
- package/src/duckdb/src/execution/physical_plan/plan_aggregate.cpp +43 -10
- package/src/duckdb/src/execution/physical_plan/plan_cte.cpp +33 -0
- package/src/duckdb/src/execution/physical_plan/plan_recursive_cte.cpp +25 -4
- package/src/duckdb/src/execution/physical_plan_generator.cpp +4 -0
- package/src/duckdb/src/execution/radix_partitioned_hashtable.cpp +290 -43
- package/src/duckdb/src/execution/window_segment_tree.cpp +286 -129
- package/src/duckdb/src/function/aggregate/sorted_aggregate_function.cpp +2 -1
- package/src/duckdb/src/function/function.cpp +3 -1
- package/src/duckdb/src/function/scalar/compressed_materialization/compress_integral.cpp +212 -0
- package/src/duckdb/src/function/scalar/compressed_materialization/compress_string.cpp +249 -0
- package/src/duckdb/src/function/scalar/compressed_materialization_functions.cpp +29 -0
- package/src/duckdb/src/function/scalar/list/list_resize.cpp +162 -0
- package/src/duckdb/src/function/scalar/nested_functions.cpp +1 -0
- package/src/duckdb/src/function/scalar/string/like.cpp +12 -4
- package/src/duckdb/src/function/scalar/system/aggregate_export.cpp +12 -5
- package/src/duckdb/src/function/table/copy_csv.cpp +8 -1
- package/src/duckdb/src/function/table/read_csv.cpp +100 -17
- package/src/duckdb/src/function/table/system/test_all_types.cpp +38 -18
- package/src/duckdb/src/function/table/table_scan.cpp +9 -0
- package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
- package/src/duckdb/src/include/duckdb/common/adbc/adbc.hpp +1 -0
- package/src/duckdb/src/include/duckdb/common/allocator.hpp +2 -0
- package/src/duckdb/src/include/duckdb/common/bswap.hpp +42 -0
- package/src/duckdb/src/include/duckdb/common/enum_util.hpp +8 -0
- package/src/duckdb/src/include/duckdb/common/enums/cte_materialize.hpp +21 -0
- package/src/duckdb/src/include/duckdb/common/enums/joinref_type.hpp +2 -1
- package/src/duckdb/src/include/duckdb/common/enums/logical_operator_type.hpp +2 -0
- package/src/duckdb/src/include/duckdb/common/enums/optimizer_type.hpp +2 -0
- package/src/duckdb/src/include/duckdb/common/enums/physical_operator_type.hpp +2 -0
- package/src/duckdb/src/include/duckdb/common/multi_file_reader.hpp +6 -4
- package/src/duckdb/src/include/duckdb/common/multi_file_reader_options.hpp +10 -42
- package/src/duckdb/src/include/duckdb/common/mutex.hpp +3 -0
- package/src/duckdb/src/include/duckdb/common/radix.hpp +9 -20
- package/src/duckdb/src/include/duckdb/common/radix_partitioning.hpp +6 -21
- package/src/duckdb/src/include/duckdb/common/row_operations/row_operations.hpp +3 -3
- package/src/duckdb/src/include/duckdb/common/sort/partition_state.hpp +13 -0
- package/src/duckdb/src/include/duckdb/common/types/batched_data_collection.hpp +3 -1
- package/src/duckdb/src/include/duckdb/common/types/column/column_data_allocator.hpp +1 -1
- package/src/duckdb/src/include/duckdb/common/types/column/column_data_collection.hpp +6 -1
- package/src/duckdb/src/include/duckdb/common/types/column/column_data_collection_segment.hpp +1 -1
- package/src/duckdb/src/include/duckdb/common/types/column/column_data_scan_states.hpp +3 -1
- package/src/duckdb/src/include/duckdb/common/types/data_chunk.hpp +1 -1
- package/src/duckdb/src/include/duckdb/common/types/date.hpp +7 -5
- package/src/duckdb/src/include/duckdb/common/types/list_segment.hpp +6 -8
- package/src/duckdb/src/include/duckdb/common/types/row/partitioned_tuple_data.hpp +0 -1
- package/src/duckdb/src/include/duckdb/common/types/row/tuple_data_collection.hpp +1 -0
- package/src/duckdb/src/include/duckdb/common/types/row/tuple_data_states.hpp +3 -0
- package/src/duckdb/src/include/duckdb/common/types/string_type.hpp +9 -0
- package/src/duckdb/src/include/duckdb/core_functions/aggregate/algebraic/corr.hpp +4 -4
- package/src/duckdb/src/include/duckdb/core_functions/aggregate/algebraic/covar.hpp +3 -1
- package/src/duckdb/src/include/duckdb/core_functions/aggregate/regression/regr_count.hpp +1 -0
- package/src/duckdb/src/include/duckdb/core_functions/aggregate/regression/regr_slope.hpp +3 -3
- package/src/duckdb/src/include/duckdb/core_functions/scalar/date_functions.hpp +24 -6
- package/src/duckdb/src/include/duckdb/execution/aggregate_hashtable.hpp +21 -3
- package/src/duckdb/src/include/duckdb/execution/executor.hpp +3 -0
- package/src/duckdb/src/include/duckdb/execution/index/art/art.hpp +4 -5
- package/src/duckdb/src/include/duckdb/execution/index/art/iterator.hpp +31 -27
- package/src/duckdb/src/include/duckdb/execution/index/art/leaf.hpp +6 -14
- package/src/duckdb/src/include/duckdb/execution/index/art/node.hpp +4 -10
- package/src/duckdb/src/include/duckdb/execution/index/art/node16.hpp +3 -6
- package/src/duckdb/src/include/duckdb/execution/index/art/node256.hpp +3 -6
- package/src/duckdb/src/include/duckdb/execution/index/art/node4.hpp +5 -8
- package/src/duckdb/src/include/duckdb/execution/index/art/node48.hpp +3 -6
- package/src/duckdb/src/include/duckdb/execution/index/art/prefix.hpp +63 -52
- package/src/duckdb/src/include/duckdb/execution/operator/join/physical_asof_join.hpp +2 -10
- package/src/duckdb/src/include/duckdb/execution/operator/persistent/base_csv_reader.hpp +2 -2
- package/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_line_info.hpp +4 -3
- package/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_reader_options.hpp +8 -1
- package/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_rejects_table.hpp +36 -0
- package/src/duckdb/src/include/duckdb/execution/operator/persistent/parallel_csv_reader.hpp +1 -1
- package/src/duckdb/src/include/duckdb/execution/operator/scan/physical_column_data_scan.hpp +10 -0
- package/src/duckdb/src/include/duckdb/execution/operator/set/physical_cte.hpp +62 -0
- package/src/duckdb/src/include/duckdb/execution/operator/set/physical_recursive_cte.hpp +8 -2
- package/src/duckdb/src/include/duckdb/execution/partitionable_hashtable.hpp +5 -1
- package/src/duckdb/src/include/duckdb/execution/physical_operator.hpp +3 -0
- package/src/duckdb/src/include/duckdb/execution/physical_plan_generator.hpp +3 -0
- package/src/duckdb/src/include/duckdb/execution/radix_partitioned_hashtable.hpp +10 -3
- package/src/duckdb/src/include/duckdb/execution/window_segment_tree.hpp +51 -40
- package/src/duckdb/src/include/duckdb/function/aggregate_function.hpp +1 -1
- package/src/duckdb/src/include/duckdb/function/aggregate_state.hpp +2 -2
- package/src/duckdb/src/include/duckdb/function/built_in_functions.hpp +1 -0
- package/src/duckdb/src/include/duckdb/function/scalar/compressed_materialization_functions.hpp +49 -0
- package/src/duckdb/src/include/duckdb/function/scalar/list/contains_or_position.hpp +1 -1
- package/src/duckdb/src/include/duckdb/function/scalar/nested_functions.hpp +5 -0
- package/src/duckdb/src/include/duckdb/function/scalar/string_functions.hpp +2 -0
- package/src/duckdb/src/include/duckdb/function/table/system_functions.hpp +1 -1
- package/src/duckdb/src/include/duckdb/main/client_config.hpp +3 -0
- package/src/duckdb/src/include/duckdb/main/config.hpp +2 -0
- package/src/duckdb/src/include/duckdb/main/connection.hpp +1 -2
- package/src/duckdb/src/include/duckdb/main/settings.hpp +21 -1
- package/src/duckdb/src/include/duckdb/optimizer/column_binding_replacer.hpp +47 -0
- package/src/duckdb/src/include/duckdb/optimizer/compressed_materialization.hpp +132 -0
- package/src/duckdb/src/include/duckdb/optimizer/deliminator.hpp +13 -16
- package/src/duckdb/src/include/duckdb/optimizer/filter_pushdown.hpp +3 -0
- package/src/duckdb/src/include/duckdb/optimizer/join_order/cardinality_estimator.hpp +1 -1
- package/src/duckdb/src/include/duckdb/optimizer/join_order/estimated_properties.hpp +10 -1
- package/src/duckdb/src/include/duckdb/optimizer/join_order/join_order_optimizer.hpp +1 -1
- package/src/duckdb/src/include/duckdb/optimizer/join_order/join_relation.hpp +1 -1
- package/src/duckdb/src/include/duckdb/optimizer/join_order/query_graph.hpp +3 -0
- package/src/duckdb/src/include/duckdb/optimizer/matcher/set_matcher.hpp +13 -0
- package/src/duckdb/src/include/duckdb/optimizer/optimizer.hpp +3 -0
- package/src/duckdb/src/include/duckdb/optimizer/remove_duplicate_groups.hpp +40 -0
- package/src/duckdb/src/include/duckdb/optimizer/statistics_propagator.hpp +11 -3
- package/src/duckdb/src/include/duckdb/optimizer/topn_optimizer.hpp +2 -0
- package/src/duckdb/src/include/duckdb/parallel/pipeline.hpp +2 -0
- package/src/duckdb/src/include/duckdb/parallel/task_scheduler.hpp +5 -0
- package/src/duckdb/src/include/duckdb/parser/common_table_expression_info.hpp +2 -0
- package/src/duckdb/src/include/duckdb/parser/query_node/cte_node.hpp +54 -0
- package/src/duckdb/src/include/duckdb/parser/query_node/list.hpp +1 -0
- package/src/duckdb/src/include/duckdb/parser/query_node.hpp +2 -1
- package/src/duckdb/src/include/duckdb/parser/tokens.hpp +1 -0
- package/src/duckdb/src/include/duckdb/parser/transformer.hpp +15 -8
- package/src/duckdb/src/include/duckdb/planner/binder.hpp +8 -5
- package/src/duckdb/src/include/duckdb/planner/bound_tokens.hpp +1 -0
- package/src/duckdb/src/include/duckdb/planner/column_binding.hpp +4 -0
- package/src/duckdb/src/include/duckdb/planner/constraints/bound_unique_constraint.hpp +3 -3
- package/src/duckdb/src/include/duckdb/planner/expression_binder/lateral_binder.hpp +0 -2
- package/src/duckdb/src/include/duckdb/planner/logical_tokens.hpp +1 -0
- package/src/duckdb/src/include/duckdb/planner/operator/list.hpp +2 -1
- package/src/duckdb/src/include/duckdb/planner/operator/logical_comparison_join.hpp +5 -5
- package/src/duckdb/src/include/duckdb/planner/operator/logical_cteref.hpp +7 -2
- package/src/duckdb/src/include/duckdb/planner/operator/logical_dependent_join.hpp +43 -0
- package/src/duckdb/src/include/duckdb/planner/operator/logical_materialized_cte.hpp +49 -0
- package/src/duckdb/src/include/duckdb/planner/operator/logical_recursive_cte.hpp +5 -4
- package/src/duckdb/src/include/duckdb/planner/query_node/bound_cte_node.hpp +44 -0
- package/src/duckdb/src/include/duckdb/planner/query_node/list.hpp +1 -0
- package/src/duckdb/src/include/duckdb/planner/subquery/flatten_dependent_join.hpp +2 -2
- package/src/duckdb/src/include/duckdb/planner/subquery/has_correlated_expressions.hpp +4 -1
- package/src/duckdb/src/include/duckdb/planner/subquery/recursive_dependent_join_planner.hpp +31 -0
- package/src/duckdb/src/include/duckdb/planner/subquery/rewrite_correlated_expressions.hpp +8 -2
- package/src/duckdb/src/include/duckdb/planner/tableref/bound_cteref.hpp +5 -2
- package/src/duckdb/src/include/duckdb/storage/arena_allocator.hpp +1 -1
- package/src/duckdb/src/include/duckdb/storage/block_manager.hpp +3 -3
- package/src/duckdb/src/include/duckdb/storage/data_table.hpp +1 -1
- package/src/duckdb/src/include/duckdb/storage/object_cache.hpp +22 -0
- package/src/duckdb/src/include/duckdb/storage/single_file_block_manager.hpp +2 -0
- package/src/duckdb/src/include/duckdb/storage/statistics/string_stats.hpp +4 -0
- package/src/duckdb/src/include/duckdb/storage/table/chunk_info.hpp +3 -0
- package/src/duckdb/src/include/duckdb/storage/table/row_group.hpp +3 -2
- package/src/duckdb/src/include/duckdb/storage/table/row_group_collection.hpp +1 -3
- package/src/duckdb/src/include/duckdb/transaction/local_storage.hpp +2 -3
- package/src/duckdb/src/include/duckdb.h +28 -0
- package/src/duckdb/src/main/capi/arrow-c.cpp +155 -1
- package/src/duckdb/src/main/capi/duckdb_value-c.cpp +1 -1
- package/src/duckdb/src/main/config.cpp +2 -0
- package/src/duckdb/src/main/database.cpp +1 -1
- package/src/duckdb/src/main/extension/extension_helper.cpp +96 -89
- package/src/duckdb/src/main/settings/settings.cpp +40 -18
- package/src/duckdb/src/optimizer/column_binding_replacer.cpp +43 -0
- package/src/duckdb/src/optimizer/column_lifetime_analyzer.cpp +1 -2
- package/src/duckdb/src/optimizer/compressed_materialization/compress_aggregate.cpp +140 -0
- package/src/duckdb/src/optimizer/compressed_materialization/compress_distinct.cpp +42 -0
- package/src/duckdb/src/optimizer/compressed_materialization/compress_order.cpp +65 -0
- package/src/duckdb/src/optimizer/compressed_materialization.cpp +478 -0
- package/src/duckdb/src/optimizer/deliminator.cpp +176 -321
- package/src/duckdb/src/optimizer/filter_pushdown.cpp +9 -0
- package/src/duckdb/src/optimizer/join_order/estimated_properties.cpp +7 -0
- package/src/duckdb/src/optimizer/join_order/join_node.cpp +2 -2
- package/src/duckdb/src/optimizer/join_order/join_order_optimizer.cpp +113 -82
- package/src/duckdb/src/optimizer/join_order/join_relation_set.cpp +2 -6
- package/src/duckdb/src/optimizer/join_order/query_graph.cpp +22 -14
- package/src/duckdb/src/optimizer/optimizer.cpp +51 -14
- package/src/duckdb/src/optimizer/pushdown/pushdown_cross_product.cpp +5 -5
- package/src/duckdb/src/optimizer/pushdown/pushdown_get.cpp +0 -1
- package/src/duckdb/src/optimizer/remove_duplicate_groups.cpp +127 -0
- package/src/duckdb/src/optimizer/remove_unused_columns.cpp +4 -0
- package/src/duckdb/src/optimizer/rule/regex_optimizations.cpp +154 -15
- package/src/duckdb/src/optimizer/statistics/operator/propagate_join.cpp +65 -8
- package/src/duckdb/src/optimizer/statistics/operator/propagate_order.cpp +1 -1
- package/src/duckdb/src/optimizer/statistics_propagator.cpp +7 -5
- package/src/duckdb/src/optimizer/topn_optimizer.cpp +20 -10
- package/src/duckdb/src/parallel/executor.cpp +15 -0
- package/src/duckdb/src/parallel/pipeline_executor.cpp +7 -6
- package/src/duckdb/src/parallel/task_scheduler.cpp +11 -2
- package/src/duckdb/src/parser/common_table_expression_info.cpp +2 -0
- package/src/duckdb/src/parser/expression/lambda_expression.cpp +1 -1
- package/src/duckdb/src/parser/parsed_expression_iterator.cpp +7 -0
- package/src/duckdb/src/parser/query_node/cte_node.cpp +75 -0
- package/src/duckdb/src/parser/query_node.cpp +18 -1
- package/src/duckdb/src/parser/tableref/joinref.cpp +3 -0
- package/src/duckdb/src/parser/transform/expression/transform_constant.cpp +55 -3
- package/src/duckdb/src/parser/transform/expression/transform_expression.cpp +2 -0
- package/src/duckdb/src/parser/transform/expression/transform_multi_assign_reference.cpp +44 -0
- package/src/duckdb/src/parser/transform/helpers/transform_cte.cpp +19 -1
- package/src/duckdb/src/parser/transform/statement/transform_copy.cpp +13 -0
- package/src/duckdb/src/parser/transform/statement/transform_delete.cpp +6 -1
- package/src/duckdb/src/parser/transform/statement/transform_insert.cpp +6 -1
- package/src/duckdb/src/parser/transform/statement/transform_pivot_stmt.cpp +7 -2
- package/src/duckdb/src/parser/transform/statement/transform_pragma.cpp +14 -11
- package/src/duckdb/src/parser/transform/statement/transform_select_node.cpp +11 -2
- package/src/duckdb/src/parser/transform/statement/transform_update.cpp +6 -1
- package/src/duckdb/src/parser/transformer.cpp +15 -0
- package/src/duckdb/src/planner/binder/query_node/bind_cte_node.cpp +64 -0
- package/src/duckdb/src/planner/binder/query_node/plan_cte_node.cpp +26 -0
- package/src/duckdb/src/planner/binder/query_node/plan_recursive_cte_node.cpp +5 -5
- package/src/duckdb/src/planner/binder/query_node/plan_setop.cpp +4 -4
- package/src/duckdb/src/planner/binder/query_node/plan_subquery.cpp +32 -29
- package/src/duckdb/src/planner/binder/tableref/bind_basetableref.cpp +11 -2
- package/src/duckdb/src/planner/binder/tableref/bind_joinref.cpp +32 -5
- package/src/duckdb/src/planner/binder/tableref/bind_pivot.cpp +116 -50
- package/src/duckdb/src/planner/binder/tableref/plan_cteref.cpp +1 -1
- package/src/duckdb/src/planner/binder/tableref/plan_joinref.cpp +61 -26
- package/src/duckdb/src/planner/binder/tableref/plan_subqueryref.cpp +3 -3
- package/src/duckdb/src/planner/binder.cpp +5 -0
- package/src/duckdb/src/planner/expression/bound_aggregate_expression.cpp +1 -1
- package/src/duckdb/src/planner/expression_binder/lateral_binder.cpp +4 -31
- package/src/duckdb/src/planner/expression_binder.cpp +3 -0
- package/src/duckdb/src/planner/expression_iterator.cpp +6 -0
- package/src/duckdb/src/planner/logical_operator.cpp +5 -0
- package/src/duckdb/src/planner/logical_operator_visitor.cpp +2 -0
- package/src/duckdb/src/planner/operator/logical_cteref.cpp +3 -1
- package/src/duckdb/src/planner/operator/logical_dependent_join.cpp +26 -0
- package/src/duckdb/src/planner/operator/logical_materialized_cte.cpp +21 -0
- package/src/duckdb/src/planner/subquery/flatten_dependent_join.cpp +90 -38
- package/src/duckdb/src/planner/subquery/has_correlated_expressions.cpp +22 -7
- package/src/duckdb/src/planner/subquery/rewrite_correlated_expressions.cpp +65 -7
- package/src/duckdb/src/storage/arena_allocator.cpp +1 -2
- package/src/duckdb/src/storage/buffer/block_manager.cpp +3 -0
- package/src/duckdb/src/storage/checkpoint_manager.cpp +3 -0
- package/src/duckdb/src/storage/compression/rle.cpp +0 -1
- package/src/duckdb/src/storage/data_table.cpp +1 -1
- package/src/duckdb/src/storage/local_storage.cpp +3 -3
- package/src/duckdb/src/storage/single_file_block_manager.cpp +23 -0
- package/src/duckdb/src/storage/statistics/string_stats.cpp +21 -2
- package/src/duckdb/src/storage/storage_info.cpp +1 -1
- package/src/duckdb/src/storage/storage_manager.cpp +7 -2
- package/src/duckdb/src/storage/table/chunk_info.cpp +17 -0
- package/src/duckdb/src/storage/table/row_group.cpp +25 -9
- package/src/duckdb/src/storage/table/row_group_collection.cpp +19 -18
- package/src/duckdb/third_party/concurrentqueue/concurrentqueue.h +2 -2
- package/src/duckdb/third_party/concurrentqueue/lightweightsemaphore.h +76 -0
- package/src/duckdb/third_party/fast_float/fast_float/fast_float.h +2 -0
- package/src/duckdb/third_party/httplib/httplib.hpp +10 -1
- package/src/duckdb/third_party/libpg_query/include/nodes/parsenodes.hpp +9 -0
- package/src/duckdb/third_party/libpg_query/include/parser/gram.hpp +2 -1
- package/src/duckdb/third_party/libpg_query/src_backend_parser_gram.cpp +12487 -12331
- package/src/duckdb/ub_extension_icu_third_party_icu_i18n.cpp +6 -6
- package/src/duckdb/ub_src_execution_index_art.cpp +0 -2
- package/src/duckdb/ub_src_execution_operator_persistent.cpp +2 -0
- package/src/duckdb/ub_src_execution_operator_set.cpp +2 -0
- package/src/duckdb/ub_src_execution_physical_plan.cpp +2 -0
- package/src/duckdb/ub_src_function_scalar.cpp +2 -0
- package/src/duckdb/ub_src_function_scalar_compressed_materialization.cpp +4 -0
- package/src/duckdb/ub_src_function_scalar_list.cpp +2 -0
- package/src/duckdb/ub_src_optimizer.cpp +6 -0
- package/src/duckdb/ub_src_optimizer_compressed_materialization.cpp +6 -0
- package/src/duckdb/ub_src_optimizer_statistics_expression.cpp +0 -2
- package/src/duckdb/ub_src_parser_query_node.cpp +2 -0
- package/src/duckdb/ub_src_parser_transform_expression.cpp +2 -0
- package/src/duckdb/ub_src_planner_binder_query_node.cpp +4 -0
- package/src/duckdb/ub_src_planner_operator.cpp +4 -0
- package/src/duckdb_node.hpp +1 -0
- package/src/statement.cpp +104 -4
- package/test/columns.test.ts +243 -0
- package/test/test_all_types.test.ts +233 -0
- package/tsconfig.json +1 -0
- package/src/duckdb/src/execution/index/art/prefix_segment.cpp +0 -42
- package/src/duckdb/src/include/duckdb/execution/index/art/prefix_segment.hpp +0 -40
- package/src/duckdb/src/optimizer/statistics/expression/propagate_and_compress.cpp +0 -118
@@ -14,6 +14,10 @@
|
|
14
14
|
#include "duckdb/planner/operator/logical_window.hpp"
|
15
15
|
#include "duckdb/function/function_binder.hpp"
|
16
16
|
#include "duckdb/planner/subquery/flatten_dependent_join.hpp"
|
17
|
+
#include "duckdb/common/enums/logical_operator_type.hpp"
|
18
|
+
#include "duckdb/planner/operator/logical_dependent_join.hpp"
|
19
|
+
#include "duckdb/planner/expression_binder/lateral_binder.hpp"
|
20
|
+
#include "duckdb/planner/subquery/recursive_dependent_join_planner.hpp"
|
17
21
|
|
18
22
|
namespace duckdb {
|
19
23
|
|
@@ -327,43 +331,43 @@ static unique_ptr<Expression> PlanCorrelatedSubquery(Binder &binder, BoundSubque
|
|
327
331
|
}
|
328
332
|
}
|
329
333
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
334
|
+
void RecursiveDependentJoinPlanner::VisitOperator(LogicalOperator &op) {
|
335
|
+
if (!op.children.empty()) {
|
336
|
+
root = std::move(op.children[0]);
|
337
|
+
D_ASSERT(root);
|
338
|
+
if (root->type == LogicalOperatorType::LOGICAL_DEPENDENT_JOIN) {
|
339
|
+
// Found a dependent join, flatten it
|
340
|
+
auto &new_root = root->Cast<LogicalDependentJoin>();
|
341
|
+
root = binder.PlanLateralJoin(std::move(new_root.children[0]), std::move(new_root.children[1]),
|
342
|
+
new_root.correlated_columns, new_root.join_type,
|
343
|
+
std::move(new_root.join_condition));
|
344
|
+
}
|
345
|
+
VisitOperatorExpressions(op);
|
346
|
+
op.children[0] = std::move(root);
|
347
|
+
for (idx_t i = 0; i < op.children.size(); i++) {
|
348
|
+
D_ASSERT(op.children[i]);
|
349
|
+
VisitOperator(*op.children[i]);
|
344
350
|
}
|
345
351
|
}
|
352
|
+
}
|
346
353
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
private:
|
352
|
-
unique_ptr<LogicalOperator> root;
|
353
|
-
Binder &binder;
|
354
|
-
};
|
354
|
+
unique_ptr<Expression> RecursiveDependentJoinPlanner::VisitReplace(BoundSubqueryExpression &expr,
|
355
|
+
unique_ptr<Expression> *expr_ptr) {
|
356
|
+
return binder.PlanSubquery(expr, root);
|
357
|
+
}
|
355
358
|
|
356
359
|
unique_ptr<Expression> Binder::PlanSubquery(BoundSubqueryExpression &expr, unique_ptr<LogicalOperator> &root) {
|
357
360
|
D_ASSERT(root);
|
358
361
|
// first we translate the QueryNode of the subquery into a logical plan
|
359
362
|
// note that we do not plan nested subqueries yet
|
360
363
|
auto sub_binder = Binder::CreateBinder(context, this);
|
361
|
-
sub_binder->
|
364
|
+
sub_binder->is_outside_flattened = false;
|
362
365
|
auto subquery_root = sub_binder->CreatePlan(*expr.subquery);
|
363
366
|
D_ASSERT(subquery_root);
|
364
367
|
|
365
368
|
// now we actually flatten the subquery
|
366
369
|
auto plan = std::move(subquery_root);
|
370
|
+
|
367
371
|
unique_ptr<Expression> result_expression;
|
368
372
|
if (!expr.IsCorrelated()) {
|
369
373
|
result_expression = PlanUncorrelatedSubquery(*this, expr, root, std::move(plan));
|
@@ -371,8 +375,8 @@ unique_ptr<Expression> Binder::PlanSubquery(BoundSubqueryExpression &expr, uniqu
|
|
371
375
|
result_expression = PlanCorrelatedSubquery(*this, expr, root, std::move(plan));
|
372
376
|
}
|
373
377
|
// finally, we recursively plan the nested subqueries (if there are any)
|
374
|
-
if (sub_binder->
|
375
|
-
|
378
|
+
if (sub_binder->has_unplanned_dependent_joins) {
|
379
|
+
RecursiveDependentJoinPlanner plan(*this);
|
376
380
|
plan.VisitOperator(*root);
|
377
381
|
}
|
378
382
|
return result_expression;
|
@@ -383,7 +387,6 @@ void Binder::PlanSubqueries(unique_ptr<Expression> &expr_ptr, unique_ptr<Logical
|
|
383
387
|
return;
|
384
388
|
}
|
385
389
|
auto &expr = *expr_ptr;
|
386
|
-
|
387
390
|
// first visit the children of the node, if any
|
388
391
|
ExpressionIterator::EnumerateChildren(expr, [&](unique_ptr<Expression> &expr) { PlanSubqueries(expr, root); });
|
389
392
|
|
@@ -391,11 +394,11 @@ void Binder::PlanSubqueries(unique_ptr<Expression> &expr_ptr, unique_ptr<Logical
|
|
391
394
|
if (expr.expression_class == ExpressionClass::BOUND_SUBQUERY) {
|
392
395
|
auto &subquery = expr.Cast<BoundSubqueryExpression>();
|
393
396
|
// subquery node! plan it
|
394
|
-
if (subquery.IsCorrelated() && !
|
397
|
+
if (subquery.IsCorrelated() && !is_outside_flattened) {
|
395
398
|
// detected a nested correlated subquery
|
396
399
|
// we don't plan it yet here, we are currently planning a subquery
|
397
400
|
// nested subqueries will only be planned AFTER the current subquery has been flattened entirely
|
398
|
-
|
401
|
+
has_unplanned_dependent_joins = true;
|
399
402
|
return;
|
400
403
|
}
|
401
404
|
expr_ptr = PlanSubquery(subquery, root);
|
@@ -411,7 +414,7 @@ unique_ptr<LogicalOperator> Binder::PlanLateralJoin(unique_ptr<LogicalOperator>
|
|
411
414
|
vector<unique_ptr<Expression>> arbitrary_expressions;
|
412
415
|
if (condition) {
|
413
416
|
// extract join conditions, if there are any
|
414
|
-
LogicalComparisonJoin::ExtractJoinConditions(join_type, left, right, std::move(condition), conditions,
|
417
|
+
LogicalComparisonJoin::ExtractJoinConditions(context, join_type, left, right, std::move(condition), conditions,
|
415
418
|
arbitrary_expressions);
|
416
419
|
}
|
417
420
|
|
@@ -45,9 +45,18 @@ unique_ptr<BoundTableRef> Binder::Bind(BaseTableRef &ref) {
|
|
45
45
|
return Bind(subquery, found_cte);
|
46
46
|
} else {
|
47
47
|
// There is a CTE binding in the BindContext.
|
48
|
-
// This can only be the case if there is a recursive CTE
|
48
|
+
// This can only be the case if there is a recursive CTE,
|
49
|
+
// or a materialized CTE present.
|
49
50
|
auto index = GenerateTableIndex();
|
50
|
-
auto
|
51
|
+
auto materialized = cte.materialized;
|
52
|
+
if (materialized == CTEMaterialize::CTE_MATERIALIZE_DEFAULT) {
|
53
|
+
#ifdef DUCKDB_ALTERNATIVE_VERIFY
|
54
|
+
materialized = CTEMaterialize::CTE_MATERIALIZE_ALWAYS;
|
55
|
+
#else
|
56
|
+
materialized = CTEMaterialize::CTE_MATERIALIZE_NEVER;
|
57
|
+
#endif
|
58
|
+
}
|
59
|
+
auto result = make_uniq<BoundCTERef>(index, ctebinding->index, materialized);
|
51
60
|
auto b = ctebinding;
|
52
61
|
auto alias = ref.alias.empty() ? ref.table_name : ref.alias;
|
53
62
|
auto names = BindContext::AliasColumnNames(alias, b->names, ref.column_name_alias);
|
@@ -130,9 +130,19 @@ unique_ptr<BoundTableRef> Binder::Bind(JoinRef &ref) {
|
|
130
130
|
{
|
131
131
|
LateralBinder binder(left_binder, context);
|
132
132
|
result->right = right_binder.Bind(*ref.right);
|
133
|
-
|
134
|
-
|
135
|
-
|
133
|
+
bool is_lateral = false;
|
134
|
+
// Store the correlated columns in the right binder in bound ref for planning of LATERALs
|
135
|
+
// Ignore the correlated columns in the left binder, flattening handles those correlations
|
136
|
+
result->correlated_columns = right_binder.correlated_columns;
|
137
|
+
// Find correlations for the current join
|
138
|
+
for (auto &cor_col : result->correlated_columns) {
|
139
|
+
if (cor_col.depth == 1) {
|
140
|
+
// Depth 1 indicates columns binding from the left indicating a lateral join
|
141
|
+
is_lateral = true;
|
142
|
+
break;
|
143
|
+
}
|
144
|
+
}
|
145
|
+
result->lateral = is_lateral;
|
136
146
|
if (result->lateral) {
|
137
147
|
// lateral join: can only be an INNER or LEFT join
|
138
148
|
if (ref.type != JoinType::INNER && ref.type != JoinType::LEFT) {
|
@@ -210,6 +220,7 @@ unique_ptr<BoundTableRef> Binder::Bind(JoinRef &ref) {
|
|
210
220
|
|
211
221
|
case JoinRefType::CROSS:
|
212
222
|
case JoinRefType::POSITIONAL:
|
223
|
+
case JoinRefType::DEPENDENT:
|
213
224
|
break;
|
214
225
|
}
|
215
226
|
extra_using_columns = RemoveDuplicateUsingColumns(extra_using_columns);
|
@@ -268,8 +279,24 @@ unique_ptr<BoundTableRef> Binder::Bind(JoinRef &ref) {
|
|
268
279
|
|
269
280
|
bind_context.AddContext(std::move(left_binder.bind_context));
|
270
281
|
bind_context.AddContext(std::move(right_binder.bind_context));
|
271
|
-
|
272
|
-
|
282
|
+
|
283
|
+
// Update the correlated columns for the parent binder
|
284
|
+
// For the left binder, depth >= 1 indicates correlations from the parent binder
|
285
|
+
for (const auto &col : left_binder.correlated_columns) {
|
286
|
+
if (col.depth >= 1) {
|
287
|
+
AddCorrelatedColumn(col);
|
288
|
+
}
|
289
|
+
}
|
290
|
+
// For the right binder, depth > 1 indicates correlations from the parent binder
|
291
|
+
// (depth = 1 indicates correlations from the left side of the join)
|
292
|
+
for (auto col : right_binder.correlated_columns) {
|
293
|
+
if (col.depth > 1) {
|
294
|
+
// Decrement the depth to account for the effect of the lateral binder
|
295
|
+
col.depth--;
|
296
|
+
AddCorrelatedColumn(col);
|
297
|
+
}
|
298
|
+
}
|
299
|
+
|
273
300
|
for (auto &condition : extra_conditions) {
|
274
301
|
if (ref.condition) {
|
275
302
|
ref.condition = make_uniq<ConjunctionExpression>(ExpressionType::CONJUNCTION_AND, std::move(ref.condition),
|
@@ -18,6 +18,7 @@
|
|
18
18
|
#include "duckdb/planner/tableref/bound_pivotref.hpp"
|
19
19
|
#include "duckdb/planner/expression/bound_aggregate_expression.hpp"
|
20
20
|
#include "duckdb/main/client_config.hpp"
|
21
|
+
#include "duckdb/catalog/catalog_entry/aggregate_function_catalog_entry.hpp"
|
21
22
|
|
22
23
|
namespace duckdb {
|
23
24
|
|
@@ -66,18 +67,10 @@ static void ExtractPivotExpressions(ParsedExpression &expr, case_insensitive_set
|
|
66
67
|
expr, [&](ParsedExpression &child) { ExtractPivotExpressions(child, handled_columns); });
|
67
68
|
}
|
68
69
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
vector<string> internal_aggregate_names;
|
74
|
-
};
|
75
|
-
|
76
|
-
static unique_ptr<SelectNode> PivotInitialAggregate(PivotBindState &bind_state, PivotRef &ref,
|
77
|
-
vector<unique_ptr<ParsedExpression>> all_columns,
|
78
|
-
const case_insensitive_set_t &handled_columns) {
|
79
|
-
auto subquery_stage1 = make_uniq<SelectNode>();
|
80
|
-
subquery_stage1->from_table = std::move(ref.source);
|
70
|
+
static unique_ptr<SelectNode> ConstructInitialGrouping(PivotRef &ref, vector<unique_ptr<ParsedExpression>> all_columns,
|
71
|
+
const case_insensitive_set_t &handled_columns) {
|
72
|
+
auto subquery = make_uniq<SelectNode>();
|
73
|
+
subquery->from_table = std::move(ref.source);
|
81
74
|
if (ref.groups.empty()) {
|
82
75
|
// if rows are not specified any columns that are not pivoted/aggregated on are added to the GROUP BY clause
|
83
76
|
for (auto &entry : all_columns) {
|
@@ -87,19 +80,74 @@ static unique_ptr<SelectNode> PivotInitialAggregate(PivotBindState &bind_state,
|
|
87
80
|
auto &columnref = entry->Cast<ColumnRefExpression>();
|
88
81
|
if (handled_columns.find(columnref.GetColumnName()) == handled_columns.end()) {
|
89
82
|
// not handled - add to grouping set
|
90
|
-
|
91
|
-
make_uniq<ConstantExpression>(Value::INTEGER(
|
92
|
-
|
83
|
+
subquery->groups.group_expressions.push_back(
|
84
|
+
make_uniq<ConstantExpression>(Value::INTEGER(subquery->select_list.size() + 1)));
|
85
|
+
subquery->select_list.push_back(make_uniq<ColumnRefExpression>(columnref.GetColumnName()));
|
93
86
|
}
|
94
87
|
}
|
95
88
|
} else {
|
96
89
|
// if rows are specified only the columns mentioned in rows are added as groups
|
97
90
|
for (auto &row : ref.groups) {
|
98
|
-
|
99
|
-
make_uniq<ConstantExpression>(Value::INTEGER(
|
100
|
-
|
91
|
+
subquery->groups.group_expressions.push_back(
|
92
|
+
make_uniq<ConstantExpression>(Value::INTEGER(subquery->select_list.size() + 1)));
|
93
|
+
subquery->select_list.push_back(make_uniq<ColumnRefExpression>(row));
|
101
94
|
}
|
102
95
|
}
|
96
|
+
return subquery;
|
97
|
+
}
|
98
|
+
|
99
|
+
static unique_ptr<SelectNode> PivotFilteredAggregate(PivotRef &ref, vector<unique_ptr<ParsedExpression>> all_columns,
|
100
|
+
const case_insensitive_set_t &handled_columns,
|
101
|
+
vector<PivotValueElement> pivot_values) {
|
102
|
+
auto subquery = ConstructInitialGrouping(ref, std::move(all_columns), handled_columns);
|
103
|
+
|
104
|
+
// push the filtered aggregates
|
105
|
+
for (auto &pivot_value : pivot_values) {
|
106
|
+
unique_ptr<ParsedExpression> filter;
|
107
|
+
idx_t pivot_value_idx = 0;
|
108
|
+
for (auto &pivot_column : ref.pivots) {
|
109
|
+
for (auto &pivot_expr : pivot_column.pivot_expressions) {
|
110
|
+
auto column_ref = make_uniq<CastExpression>(LogicalType::VARCHAR, pivot_expr->Copy());
|
111
|
+
auto constant_value = make_uniq<ConstantExpression>(pivot_value.values[pivot_value_idx++]);
|
112
|
+
auto comp_expr = make_uniq<ComparisonExpression>(ExpressionType::COMPARE_NOT_DISTINCT_FROM,
|
113
|
+
std::move(column_ref), std::move(constant_value));
|
114
|
+
if (filter) {
|
115
|
+
filter = make_uniq<ConjunctionExpression>(ExpressionType::CONJUNCTION_AND, std::move(filter),
|
116
|
+
std::move(comp_expr));
|
117
|
+
} else {
|
118
|
+
filter = std::move(comp_expr);
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
122
|
+
for (auto &aggregate : ref.aggregates) {
|
123
|
+
auto copied_aggr = aggregate->Copy();
|
124
|
+
auto &aggr = copied_aggr->Cast<FunctionExpression>();
|
125
|
+
aggr.filter = filter->Copy();
|
126
|
+
auto &aggr_name = aggregate->alias;
|
127
|
+
auto name = pivot_value.name;
|
128
|
+
if (ref.aggregates.size() > 1 || !aggr_name.empty()) {
|
129
|
+
// if there are multiple aggregates specified we add the name of the aggregate as well
|
130
|
+
name += "_" + (aggr_name.empty() ? aggregate->GetName() : aggr_name);
|
131
|
+
}
|
132
|
+
aggr.alias = name;
|
133
|
+
subquery->select_list.push_back(std::move(copied_aggr));
|
134
|
+
}
|
135
|
+
}
|
136
|
+
return subquery;
|
137
|
+
}
|
138
|
+
|
139
|
+
struct PivotBindState {
|
140
|
+
vector<string> internal_group_names;
|
141
|
+
vector<string> group_names;
|
142
|
+
vector<string> aggregate_names;
|
143
|
+
vector<string> internal_aggregate_names;
|
144
|
+
};
|
145
|
+
|
146
|
+
static unique_ptr<SelectNode> PivotInitialAggregate(PivotBindState &bind_state, PivotRef &ref,
|
147
|
+
vector<unique_ptr<ParsedExpression>> all_columns,
|
148
|
+
const case_insensitive_set_t &handled_columns) {
|
149
|
+
auto subquery_stage1 = ConstructInitialGrouping(ref, std::move(all_columns), handled_columns);
|
150
|
+
|
103
151
|
idx_t group_count = 0;
|
104
152
|
for (auto &expr : subquery_stage1->select_list) {
|
105
153
|
bind_state.group_names.push_back(expr->GetName());
|
@@ -134,6 +182,15 @@ static unique_ptr<SelectNode> PivotInitialAggregate(PivotBindState &bind_state,
|
|
134
182
|
return subquery_stage1;
|
135
183
|
}
|
136
184
|
|
185
|
+
unique_ptr<ParsedExpression> ConstructPivotExpression(unique_ptr<ParsedExpression> pivot_expr) {
|
186
|
+
auto cast = make_uniq<CastExpression>(LogicalType::VARCHAR, std::move(pivot_expr));
|
187
|
+
vector<unique_ptr<ParsedExpression>> coalesce_children;
|
188
|
+
coalesce_children.push_back(std::move(cast));
|
189
|
+
coalesce_children.push_back(make_uniq<ConstantExpression>(Value("NULL")));
|
190
|
+
auto coalesce = make_uniq<OperatorExpression>(ExpressionType::OPERATOR_COALESCE, std::move(coalesce_children));
|
191
|
+
return std::move(coalesce);
|
192
|
+
}
|
193
|
+
|
137
194
|
static unique_ptr<SelectNode> PivotListAggregate(PivotBindState &bind_state, PivotRef &ref,
|
138
195
|
unique_ptr<SelectNode> subquery_stage1) {
|
139
196
|
auto subquery_stage2 = make_uniq<SelectNode>();
|
@@ -166,13 +223,7 @@ static unique_ptr<SelectNode> PivotListAggregate(PivotBindState &bind_state, Piv
|
|
166
223
|
for (auto &pivot : ref.pivots) {
|
167
224
|
for (auto &pivot_expr : pivot.pivot_expressions) {
|
168
225
|
// coalesce(pivot::VARCHAR, 'NULL')
|
169
|
-
auto
|
170
|
-
vector<unique_ptr<ParsedExpression>> coalesce_children;
|
171
|
-
coalesce_children.push_back(std::move(cast));
|
172
|
-
coalesce_children.push_back(make_uniq<ConstantExpression>(Value("NULL")));
|
173
|
-
auto coalesce =
|
174
|
-
make_uniq<OperatorExpression>(ExpressionType::OPERATOR_COALESCE, std::move(coalesce_children));
|
175
|
-
|
226
|
+
auto coalesce = ConstructPivotExpression(std::move(pivot_expr));
|
176
227
|
if (!expr) {
|
177
228
|
expr = std::move(coalesce);
|
178
229
|
} else {
|
@@ -250,9 +301,8 @@ unique_ptr<BoundTableRef> Binder::BindBoundPivot(PivotRef &ref) {
|
|
250
301
|
auto &aggregates = result->bound_pivot.aggregates;
|
251
302
|
ExtractPivotAggregates(*result->child, aggregates);
|
252
303
|
if (aggregates.size() != ref.bound_aggregate_names.size()) {
|
253
|
-
throw
|
254
|
-
|
255
|
-
ref.bound_aggregate_names.size(), aggregates.size());
|
304
|
+
throw InternalException("Pivot aggregate count mismatch (expected %llu, found %llu)",
|
305
|
+
ref.bound_aggregate_names.size(), aggregates.size());
|
256
306
|
}
|
257
307
|
|
258
308
|
vector<string> child_names;
|
@@ -313,9 +363,12 @@ unique_ptr<SelectNode> Binder::BindPivot(PivotRef &ref, vector<unique_ptr<Parsed
|
|
313
363
|
if (aggr->IsWindow()) {
|
314
364
|
throw BinderException(FormatError(*aggr, "Pivot expression cannot contain window functions"));
|
315
365
|
}
|
366
|
+
// bind the function as an aggregate to ensure it is an aggregate and not a scalar function
|
367
|
+
auto &aggr_function = aggr->Cast<FunctionExpression>();
|
368
|
+
(void)Catalog::GetEntry<AggregateFunctionCatalogEntry>(context, aggr_function.catalog, aggr_function.schema,
|
369
|
+
aggr_function.function_name);
|
316
370
|
ExtractPivotExpressions(*aggr, handled_columns);
|
317
371
|
}
|
318
|
-
value_set_t pivots;
|
319
372
|
|
320
373
|
// first add all pivots to the set of handled columns, and check for duplicates
|
321
374
|
idx_t total_pivots = 1;
|
@@ -362,40 +415,53 @@ unique_ptr<SelectNode> Binder::BindPivot(PivotRef &ref, vector<unique_ptr<Parsed
|
|
362
415
|
pivots.insert(val);
|
363
416
|
}
|
364
417
|
}
|
365
|
-
auto
|
418
|
+
auto &client_config = ClientConfig::GetConfig(context);
|
419
|
+
auto pivot_limit = client_config.pivot_limit;
|
366
420
|
if (total_pivots >= pivot_limit) {
|
367
421
|
throw BinderException("Pivot column limit of %llu exceeded. Use SET pivot_limit=X to increase the limit.",
|
368
|
-
|
422
|
+
client_config.pivot_limit);
|
369
423
|
}
|
370
424
|
|
371
425
|
// construct the required pivot values recursively
|
372
426
|
vector<PivotValueElement> pivot_values;
|
373
427
|
ConstructPivots(ref, pivot_values);
|
374
428
|
|
429
|
+
unique_ptr<SelectNode> pivot_node;
|
375
430
|
// pivots have three components
|
376
431
|
// - the pivots (i.e. future column names)
|
377
432
|
// - the groups (i.e. the future row names
|
378
433
|
// - the aggregates (i.e. the values of the pivot columns)
|
379
434
|
|
380
|
-
// executing a pivot statement
|
381
|
-
// 1)
|
382
|
-
//
|
383
|
-
//
|
384
|
-
//
|
385
|
-
//
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
435
|
+
// we have two ways of executing a pivot statement
|
436
|
+
// (1) the straightforward manner of filtered aggregates SUM(..) FILTER (pivot_value=X)
|
437
|
+
// (2) computing the aggregates once, then using LIST to group the aggregates together with the PIVOT operator
|
438
|
+
// -> filtered aggregates are faster when there are FEW pivot values
|
439
|
+
// -> LIST is faster when there are MANY pivot values
|
440
|
+
// we switch dynamically based on the number of pivots to compute
|
441
|
+
if (pivot_values.size() <= client_config.pivot_filter_threshold) {
|
442
|
+
// use a set of filtered aggregates
|
443
|
+
pivot_node = PivotFilteredAggregate(ref, std::move(all_columns), handled_columns, std::move(pivot_values));
|
444
|
+
} else {
|
445
|
+
// executing a pivot statement happens in three stages
|
446
|
+
// 1) execute the query "SELECT {groups}, {pivots}, {aggregates} FROM {from_clause} GROUP BY {groups}, {pivots}
|
447
|
+
// this computes all values that are required in the final result, but not yet in the correct orientation
|
448
|
+
// 2) execute the query "SELECT {groups}, LIST({pivots}), LIST({aggregates}) FROM [Q1] GROUP BY {groups}
|
449
|
+
// this pushes all pivots and aggregates that belong to a specific group together in an aligned manner
|
450
|
+
// 3) push a PIVOT operator, that performs the actual pivoting of the values into the different columns
|
451
|
+
|
452
|
+
PivotBindState bind_state;
|
453
|
+
// Pivot Stage 1
|
454
|
+
// SELECT {groups}, {pivots}, {aggregates} FROM {from_clause} GROUP BY {groups}, {pivots}
|
455
|
+
auto subquery_stage1 = PivotInitialAggregate(bind_state, ref, std::move(all_columns), handled_columns);
|
456
|
+
|
457
|
+
// Pivot stage 2
|
458
|
+
// SELECT {groups}, LIST({pivots}), LIST({aggregates}) FROM [Q1] GROUP BY {groups}
|
459
|
+
auto subquery_stage2 = PivotListAggregate(bind_state, ref, std::move(subquery_stage1));
|
460
|
+
|
461
|
+
// Pivot stage 3
|
462
|
+
// construct the final pivot operator
|
463
|
+
pivot_node = PivotFinalOperator(bind_state, ref, std::move(subquery_stage2), std::move(pivot_values));
|
464
|
+
}
|
399
465
|
return pivot_node;
|
400
466
|
}
|
401
467
|
|
@@ -13,7 +13,7 @@ unique_ptr<LogicalOperator> Binder::CreatePlan(BoundCTERef &ref) {
|
|
13
13
|
types.push_back(type);
|
14
14
|
}
|
15
15
|
|
16
|
-
return make_uniq<LogicalCTERef>(index, ref.cte_index, types, ref.bound_columns);
|
16
|
+
return make_uniq<LogicalCTERef>(index, ref.cte_index, types, ref.bound_columns, ref.materialized_cte);
|
17
17
|
}
|
18
18
|
|
19
19
|
} // namespace duckdb
|
@@ -10,11 +10,14 @@
|
|
10
10
|
#include "duckdb/planner/operator/logical_asof_join.hpp"
|
11
11
|
#include "duckdb/planner/operator/logical_comparison_join.hpp"
|
12
12
|
#include "duckdb/planner/operator/logical_cross_product.hpp"
|
13
|
+
#include "duckdb/planner/operator/logical_dependent_join.hpp"
|
13
14
|
#include "duckdb/planner/operator/logical_filter.hpp"
|
14
15
|
#include "duckdb/planner/operator/logical_positional_join.hpp"
|
15
16
|
#include "duckdb/planner/tableref/bound_joinref.hpp"
|
16
17
|
#include "duckdb/main/client_context.hpp"
|
17
18
|
#include "duckdb/planner/expression_binder/lateral_binder.hpp"
|
19
|
+
#include "duckdb/planner/subquery/recursive_dependent_join_planner.hpp"
|
20
|
+
#include "duckdb/execution/expression_executor.hpp"
|
18
21
|
|
19
22
|
namespace duckdb {
|
20
23
|
|
@@ -44,13 +47,11 @@ static bool CreateJoinCondition(Expression &expr, const unordered_set<idx_t> &le
|
|
44
47
|
return false;
|
45
48
|
}
|
46
49
|
|
47
|
-
void LogicalComparisonJoin::ExtractJoinConditions(
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
vector<JoinCondition> &conditions,
|
53
|
-
vector<unique_ptr<Expression>> &arbitrary_expressions) {
|
50
|
+
void LogicalComparisonJoin::ExtractJoinConditions(
|
51
|
+
ClientContext &context, JoinType type, unique_ptr<LogicalOperator> &left_child,
|
52
|
+
unique_ptr<LogicalOperator> &right_child, const unordered_set<idx_t> &left_bindings,
|
53
|
+
const unordered_set<idx_t> &right_bindings, vector<unique_ptr<Expression>> &expressions,
|
54
|
+
vector<JoinCondition> &conditions, vector<unique_ptr<Expression>> &arbitrary_expressions) {
|
54
55
|
for (auto &expr : expressions) {
|
55
56
|
auto total_side = JoinSide::GetJoinSide(*expr, left_bindings, right_bindings);
|
56
57
|
if (total_side != JoinSide::BOTH) {
|
@@ -68,6 +69,15 @@ void LogicalComparisonJoin::ExtractJoinConditions(JoinType type, unique_ptr<Logi
|
|
68
69
|
filter.expressions.push_back(std::move(expr));
|
69
70
|
continue;
|
70
71
|
}
|
72
|
+
// if the join is a LEFT JOIN and the join expression constantly evaluates to TRUE,
|
73
|
+
// then we do not add it to the arbitrary expressions
|
74
|
+
if (type == JoinType::LEFT && expr->IsFoldable()) {
|
75
|
+
Value result;
|
76
|
+
ExpressionExecutor::TryEvaluateScalar(context, *expr, result);
|
77
|
+
if (!result.IsNull() && result == Value(true)) {
|
78
|
+
continue;
|
79
|
+
}
|
80
|
+
}
|
71
81
|
} else if ((expr->type >= ExpressionType::COMPARE_EQUAL &&
|
72
82
|
expr->type <= ExpressionType::COMPARE_GREATERTHANOREQUALTO) ||
|
73
83
|
expr->type == ExpressionType::COMPARE_DISTINCT_FROM ||
|
@@ -82,7 +92,8 @@ void LogicalComparisonJoin::ExtractJoinConditions(JoinType type, unique_ptr<Logi
|
|
82
92
|
}
|
83
93
|
}
|
84
94
|
|
85
|
-
void LogicalComparisonJoin::ExtractJoinConditions(
|
95
|
+
void LogicalComparisonJoin::ExtractJoinConditions(ClientContext &context, JoinType type,
|
96
|
+
unique_ptr<LogicalOperator> &left_child,
|
86
97
|
unique_ptr<LogicalOperator> &right_child,
|
87
98
|
vector<unique_ptr<Expression>> &expressions,
|
88
99
|
vector<JoinCondition> &conditions,
|
@@ -90,11 +101,12 @@ void LogicalComparisonJoin::ExtractJoinConditions(JoinType type, unique_ptr<Logi
|
|
90
101
|
unordered_set<idx_t> left_bindings, right_bindings;
|
91
102
|
LogicalJoin::GetTableReferences(*left_child, left_bindings);
|
92
103
|
LogicalJoin::GetTableReferences(*right_child, right_bindings);
|
93
|
-
return ExtractJoinConditions(type, left_child, right_child, left_bindings, right_bindings, expressions,
|
94
|
-
arbitrary_expressions);
|
104
|
+
return ExtractJoinConditions(context, type, left_child, right_child, left_bindings, right_bindings, expressions,
|
105
|
+
conditions, arbitrary_expressions);
|
95
106
|
}
|
96
107
|
|
97
|
-
void LogicalComparisonJoin::ExtractJoinConditions(
|
108
|
+
void LogicalComparisonJoin::ExtractJoinConditions(ClientContext &context, JoinType type,
|
109
|
+
unique_ptr<LogicalOperator> &left_child,
|
98
110
|
unique_ptr<LogicalOperator> &right_child,
|
99
111
|
unique_ptr<Expression> condition, vector<JoinCondition> &conditions,
|
100
112
|
vector<unique_ptr<Expression>> &arbitrary_expressions) {
|
@@ -102,10 +114,12 @@ void LogicalComparisonJoin::ExtractJoinConditions(JoinType type, unique_ptr<Logi
|
|
102
114
|
vector<unique_ptr<Expression>> expressions;
|
103
115
|
expressions.push_back(std::move(condition));
|
104
116
|
LogicalFilter::SplitPredicates(expressions);
|
105
|
-
return ExtractJoinConditions(type, left_child, right_child, expressions, conditions,
|
117
|
+
return ExtractJoinConditions(context, type, left_child, right_child, expressions, conditions,
|
118
|
+
arbitrary_expressions);
|
106
119
|
}
|
107
120
|
|
108
|
-
unique_ptr<LogicalOperator> LogicalComparisonJoin::CreateJoin(
|
121
|
+
unique_ptr<LogicalOperator> LogicalComparisonJoin::CreateJoin(ClientContext &context, JoinType type,
|
122
|
+
JoinRefType reftype,
|
109
123
|
unique_ptr<LogicalOperator> left_child,
|
110
124
|
unique_ptr<LogicalOperator> right_child,
|
111
125
|
vector<JoinCondition> conditions,
|
@@ -216,29 +230,37 @@ static bool HasCorrelatedColumns(Expression &expression) {
|
|
216
230
|
return has_correlated_columns;
|
217
231
|
}
|
218
232
|
|
219
|
-
unique_ptr<LogicalOperator> LogicalComparisonJoin::CreateJoin(
|
233
|
+
unique_ptr<LogicalOperator> LogicalComparisonJoin::CreateJoin(ClientContext &context, JoinType type,
|
234
|
+
JoinRefType reftype,
|
220
235
|
unique_ptr<LogicalOperator> left_child,
|
221
236
|
unique_ptr<LogicalOperator> right_child,
|
222
237
|
unique_ptr<Expression> condition) {
|
223
238
|
vector<JoinCondition> conditions;
|
224
239
|
vector<unique_ptr<Expression>> arbitrary_expressions;
|
225
|
-
LogicalComparisonJoin::ExtractJoinConditions(type, left_child, right_child, std::move(condition),
|
226
|
-
arbitrary_expressions);
|
227
|
-
return LogicalComparisonJoin::CreateJoin(type, reftype, std::move(left_child), std::move(right_child),
|
240
|
+
LogicalComparisonJoin::ExtractJoinConditions(context, type, left_child, right_child, std::move(condition),
|
241
|
+
conditions, arbitrary_expressions);
|
242
|
+
return LogicalComparisonJoin::CreateJoin(context, type, reftype, std::move(left_child), std::move(right_child),
|
228
243
|
std::move(conditions), std::move(arbitrary_expressions));
|
229
244
|
}
|
230
245
|
|
231
246
|
unique_ptr<LogicalOperator> Binder::CreatePlan(BoundJoinRef &ref) {
|
247
|
+
auto old_is_outside_flattened = is_outside_flattened;
|
248
|
+
// Plan laterals from outermost to innermost
|
249
|
+
if (ref.lateral) {
|
250
|
+
// Set the flag to ensure that children do not flatten before the root
|
251
|
+
is_outside_flattened = false;
|
252
|
+
}
|
232
253
|
auto left = CreatePlan(*ref.left);
|
233
254
|
auto right = CreatePlan(*ref.right);
|
255
|
+
is_outside_flattened = old_is_outside_flattened;
|
256
|
+
|
257
|
+
// For joins, depth of the bindings will be one higher on the right because of the lateral binder
|
258
|
+
// If the current join does not have correlations between left and right, then the right bindings
|
259
|
+
// have depth 1 too high and can be reduced by 1 throughout
|
234
260
|
if (!ref.lateral && !ref.correlated_columns.empty()) {
|
235
|
-
// non-lateral join with correlated columns
|
236
|
-
// this happens if there is a join (or cross product) in a correlated subquery
|
237
|
-
// due to the lateral binder the expression depth of all correlated columns in the "ref.correlated_columns" set
|
238
|
-
// is 1 too high
|
239
|
-
// we reduce expression depth of all columns in the "ref.correlated_columns" set by 1
|
240
261
|
LateralBinder::ReduceExpressionDepth(*right, ref.correlated_columns);
|
241
262
|
}
|
263
|
+
|
242
264
|
if (ref.type == JoinType::RIGHT && ref.ref_type != JoinRefType::ASOF &&
|
243
265
|
ClientConfig::GetConfig(context).enable_optimizer) {
|
244
266
|
// we turn any right outer joins into left outer joins for optimization purposes
|
@@ -247,9 +269,22 @@ unique_ptr<LogicalOperator> Binder::CreatePlan(BoundJoinRef &ref) {
|
|
247
269
|
std::swap(left, right);
|
248
270
|
}
|
249
271
|
if (ref.lateral) {
|
250
|
-
|
251
|
-
|
252
|
-
|
272
|
+
if (!is_outside_flattened) {
|
273
|
+
// If outer dependent joins is yet to be flattened, only plan the lateral
|
274
|
+
has_unplanned_dependent_joins = true;
|
275
|
+
return LogicalDependentJoin::Create(std::move(left), std::move(right), ref.correlated_columns, ref.type,
|
276
|
+
std::move(ref.condition));
|
277
|
+
} else {
|
278
|
+
// All outer dependent joins have been planned and flattened, so plan and flatten lateral and recursively
|
279
|
+
// plan the children
|
280
|
+
auto new_plan = PlanLateralJoin(std::move(left), std::move(right), ref.correlated_columns, ref.type,
|
281
|
+
std::move(ref.condition));
|
282
|
+
if (has_unplanned_dependent_joins) {
|
283
|
+
RecursiveDependentJoinPlanner plan(*this);
|
284
|
+
plan.VisitOperator(*new_plan);
|
285
|
+
}
|
286
|
+
return new_plan;
|
287
|
+
}
|
253
288
|
}
|
254
289
|
switch (ref.ref_type) {
|
255
290
|
case JoinRefType::CROSS:
|
@@ -275,7 +310,7 @@ unique_ptr<LogicalOperator> Binder::CreatePlan(BoundJoinRef &ref) {
|
|
275
310
|
}
|
276
311
|
|
277
312
|
// now create the join operator from the join condition
|
278
|
-
auto result = LogicalComparisonJoin::CreateJoin(ref.type, ref.ref_type, std::move(left), std::move(right),
|
313
|
+
auto result = LogicalComparisonJoin::CreateJoin(context, ref.type, ref.ref_type, std::move(left), std::move(right),
|
279
314
|
std::move(ref.condition));
|
280
315
|
|
281
316
|
optional_ptr<LogicalOperator> join;
|
@@ -6,10 +6,10 @@ namespace duckdb {
|
|
6
6
|
unique_ptr<LogicalOperator> Binder::CreatePlan(BoundSubqueryRef &ref) {
|
7
7
|
// generate the logical plan for the subquery
|
8
8
|
// this happens separately from the current LogicalPlan generation
|
9
|
-
ref.binder->
|
9
|
+
ref.binder->is_outside_flattened = is_outside_flattened;
|
10
10
|
auto subquery = ref.binder->CreatePlan(*ref.subquery);
|
11
|
-
if (ref.binder->
|
12
|
-
|
11
|
+
if (ref.binder->has_unplanned_dependent_joins) {
|
12
|
+
has_unplanned_dependent_joins = true;
|
13
13
|
}
|
14
14
|
return subquery;
|
15
15
|
}
|